Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Discover] Inline shard failures warnings #161271

Merged
merged 76 commits into from Aug 10, 2023
Merged
Show file tree
Hide file tree
Changes from 42 commits
Commits
Show all changes
76 commits
Select commit Hold shift + click to select a range
eab86d5
[Discover] Intercept shard failures
jughosta Jul 5, 2023
bfd44e3
[Discover] Update deps
jughosta Jul 5, 2023
38e17fc
[Discover] Add a todo
jughosta Jul 5, 2023
86e7e16
[Discover] Rearrange the code
jughosta Jul 5, 2023
1af6f16
[Discover] Revert changes
jughosta Jul 6, 2023
d7b95ad
[Discover] Render warnings
jughosta Jul 6, 2023
5ebe38c
[Discover] Show a warning badge on dashboard
jughosta Jul 6, 2023
d37dcf0
[Discover] Handle NoResults case too
jughosta Jul 7, 2023
338559b
[Discover] Rearrange components
jughosta Jul 7, 2023
4854473
[Discover] Support long list
jughosta Jul 7, 2023
2400388
[Discover] Update tests
jughosta Jul 7, 2023
9be17a9
[Discover] Catch timeouts too
jughosta Jul 7, 2023
30ed661
[Discover] Better layout for timeouts
jughosta Jul 7, 2023
9c685f6
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 10, 2023
d11195f
[Discover] Allow to close warnings
jughosta Jul 10, 2023
993ea5c
[Discover] Inline context app shard warnings
jughosta Jul 10, 2023
e26cc30
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Jul 11, 2023
71190b6
[Discover] Update jest tests
jughosta Jul 11, 2023
4940876
[Discover] Add tests
jughosta Jul 11, 2023
e04aaca
[Discover] Simplify imports
jughosta Jul 11, 2023
bdac782
[Discover] Add tests
jughosta Jul 12, 2023
46361a0
[Discover] Update warning mocks
jughosta Jul 12, 2023
f5485b2
[Discover] Add more tests
jughosta Jul 12, 2023
3f102cf
[Discover] Add a test for the dashboard badge
jughosta Jul 12, 2023
932e345
[Discover] Update snapshots
jughosta Jul 12, 2023
369d135
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Jul 17, 2023
939b146
[Discover] Update adapters
jughosta Jul 17, 2023
b9de037
Update src/plugins/discover/public/application/main/components/no_res…
jughosta Jul 17, 2023
6fc2927
Merge remote-tracking branch 'origin/155216-inline-shard-error' into …
jughosta Jul 17, 2023
a28330d
[Discover] Update type
jughosta Jul 17, 2023
25a9d81
Update src/plugins/discover/public/utils/get_search_response_intercep…
jughosta Jul 17, 2023
03bcdfc
Update src/plugins/discover/public/components/common/warnings_callout…
jughosta Jul 17, 2023
c2ccd0f
[Discover] Remove sass styles and fragment
jughosta Jul 17, 2023
e83ac9d
Merge remote-tracking branch 'origin/155216-inline-shard-error' into …
jughosta Jul 17, 2023
38df2fe
[Discover] Fix icon and title alignment
jughosta Jul 17, 2023
70509c9
[Discover] Fit content for smaller screens
jughosta Jul 17, 2023
64a368b
[Discover] Make callouts scrollable
jughosta Jul 17, 2023
5b80495
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 17, 2023
4fb3d94
Merge remote-tracking branch 'origin/155216-inline-shard-error' into …
jughosta Jul 17, 2023
6ce4053
[Discover] Update jest snapshots
jughosta Jul 17, 2023
bc9d634
[Discover] Fix scollable container
jughosta Jul 18, 2023
65bc4fa
[Discover] Update jest snapshots
jughosta Jul 18, 2023
e2172ea
Merge main
jughosta Jul 19, 2023
76dc8f0
[Discover] Update comment
jughosta Jul 19, 2023
5e521c2
[CI] Auto-commit changed files from 'node scripts/eslint --no-cache -…
kibanamachine Jul 19, 2023
77e667a
[Discover] Extract warnings into a new package
jughosta Jul 20, 2023
f604b0b
[Discover] Suppress shards failure toasts for hits number request
jughosta Jul 24, 2023
c5111d4
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 24, 2023
fbc0053
[CI] Auto-commit changed files from 'node scripts/lint_ts_projects --…
kibanamachine Jul 24, 2023
f1341ef
[CI] Auto-commit changed files from 'node scripts/generate codeowners'
kibanamachine Jul 24, 2023
cc4973a
[Discover] Fix imports
jughosta Jul 24, 2023
2d05b3d
[Discover] Update tests
jughosta Jul 24, 2023
2dea556
[Discover] Fix mock imports
jughosta Jul 24, 2023
4cb789b
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 25, 2023
d6930c6
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 25, 2023
d9f0fe4
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Jul 27, 2023
412f5bc
[Discover] Add a comment
jughosta Jul 27, 2023
39f4910
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 27, 2023
bd880bc
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 31, 2023
50271de
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 31, 2023
40a89ab
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 31, 2023
72253ab
Merge branch 'main' into 155216-inline-shard-error
jughosta Jul 31, 2023
fcb44ff
[CI] Auto-commit changed files from 'node scripts/precommit_hook.js -…
kibanamachine Jul 31, 2023
2696592
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Aug 2, 2023
c9ad004
[Discover] Show warning badge outside of grid in a saved search panel
jughosta Aug 2, 2023
e640e68
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Aug 2, 2023
fb6eafe
[Discover] Add comments
jughosta Aug 2, 2023
5249333
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Aug 3, 2023
b7dd1ef
[Discover] Update old callout
jughosta Aug 3, 2023
e0a581b
[Discover] Update warning empty prompt
jughosta Aug 3, 2023
da0bd76
[Discover] Update tests
jughosta Aug 3, 2023
99b98c8
Merge remote-tracking branch 'upstream/main' into 155216-inline-shard…
jughosta Aug 4, 2023
7a3331f
[Discover] Update error callout
jughosta Aug 4, 2023
4c0358a
Merge branch 'main' into 155216-inline-shard-error
jughosta Aug 8, 2023
ed630be
Merge branch 'main' into 155216-inline-shard-error
jughosta Aug 8, 2023
2e3731e
Merge branch 'main' into 155216-inline-shard-error
jughosta Aug 10, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
16 changes: 16 additions & 0 deletions src/plugins/data/common/search/search_source/search_source.ts
Expand Up @@ -543,6 +543,8 @@ export class SearchSource {

return search({ params, indexType: searchRequest.indexType }, options).pipe(
switchMap((response) => {
// For testing timeout messages in UI, uncomment the next line
// response.rawResponse.timed_out = true;
return new Observable<IKibanaSearchResponse<unknown>>((obs) => {
if (isErrorResponse(response)) {
obs.error(response);
Expand Down Expand Up @@ -916,6 +918,20 @@ export class SearchSource {
};
body.query = buildEsQuery(index, query, filters, esQueryConfigs);

// For testing shard failure messages in UI, uncomment the next block
// body.query = {
// error_query: {
// indices: [
// {
// name: 'kibana_sample_data_logs',
// shard_ids: [0, 1],
// error_type: 'exception',
// message: 'Testing shard failures!',
// },
// ],
// },
// };

if (highlightAll && body.query) {
body.highlight = getHighlightRequest(getConfig(UI_SETTINGS.DOC_HIGHLIGHT));
delete searchRequest.highlightAll;
Expand Down
2 changes: 2 additions & 0 deletions src/plugins/discover/common/constants.ts
Expand Up @@ -12,3 +12,5 @@ export enum VIEW_MODE {
DOCUMENT_LEVEL = 'documents',
AGGREGATED_LEVEL = 'aggregated',
}

export const DISABLE_SHARD_FAILURE_WARNING = true;
50 changes: 50 additions & 0 deletions src/plugins/discover/public/__mocks__/search_response_warnings.ts
@@ -0,0 +1,50 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0 and the Server Side Public License, v 1; you may not use this file except
* in compliance with, at your election, the Elastic License 2.0 or the Server
* Side Public License, v 1.
*/

import type { SearchResponseWarning } from '@kbn/data-plugin/public';

export const searchResponseTimeoutWarningMock: SearchResponseWarning = {
type: 'timed_out',
message: 'Data might be incomplete because your request timed out',
reason: undefined,
};

export const searchResponseShardFailureWarningMock: SearchResponseWarning = {
type: 'shard_failure',
message: '3 of 4 shards failed',
text: 'The data might be incomplete or wrong.',
reason: {
type: 'illegal_argument_exception',
reason: 'Field [__anonymous_] of type [boolean] does not support custom formats',
},
};

export const searchResponseWarningsMock: SearchResponseWarning[] = [
searchResponseTimeoutWarningMock,
searchResponseShardFailureWarningMock,
{
type: 'shard_failure',
message: '3 of 4 shards failed',
text: 'The data might be incomplete or wrong.',
reason: {
type: 'query_shard_exception',
reason:
'failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!',
},
},
{
type: 'shard_failure',
message: '1 of 4 shards failed',
text: 'The data might be incomplete or wrong.',
reason: {
type: 'query_shard_exception',
reason:
'failed to create query: [.ds-kibana_sample_data_logs-2023.07.11-000001][0] Testing shard failures!',
},
},
];
16 changes: 16 additions & 0 deletions src/plugins/discover/public/application/context/context_app.tsx
Expand Up @@ -30,6 +30,7 @@ import { SurrDocType } from './services/context';
import { DocViewFilterFn } from '../../services/doc_views/doc_views_types';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import { getRootBreadcrumbs } from '../../utils/breadcrumbs';
import { removeInterceptedWarningDuplicates } from '../../utils/get_search_response_intercepted_warnings';

const ContextAppContentMemoized = memo(ContextAppContent);

Expand Down Expand Up @@ -172,6 +173,20 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) =>
[fetchedState.predecessors, fetchedState.anchor, fetchedState.successors]
);

const interceptedWarnings = useMemo(
() =>
removeInterceptedWarningDuplicates([
...(fetchedState.predecessorsInterceptedWarnings || []),
...(fetchedState.anchorInterceptedWarnings || []),
...(fetchedState.successorsInterceptedWarnings || []),
]),
[
fetchedState.predecessorsInterceptedWarnings,
fetchedState.anchorInterceptedWarnings,
fetchedState.successorsInterceptedWarnings,
]
);

const addFilter = useCallback(
async (field: DataViewField | string, values: unknown, operation: string) => {
const newFilters = generateFilters(filterManager, field, values, operation, dataView);
Expand Down Expand Up @@ -251,6 +266,7 @@ export const ContextApp = ({ dataView, anchorId, referrer }: ContextAppProps) =>
anchorStatus={fetchedState.anchorStatus.value}
predecessorsStatus={fetchedState.predecessorsStatus.value}
successorsStatus={fetchedState.successorsStatus.value}
interceptedWarnings={interceptedWarnings}
/>
</EuiPageBody>
</EuiPage>
Expand Down
Expand Up @@ -8,7 +8,7 @@

import React, { useState, Fragment, useMemo, useCallback } from 'react';
import { FormattedMessage } from '@kbn/i18n-react';
import { EuiHorizontalRule, EuiText } from '@elastic/eui';
import { EuiHorizontalRule, EuiSpacer, EuiText } from '@elastic/eui';
import type { DataView } from '@kbn/data-views-plugin/public';
import { SortDirection } from '@kbn/data-plugin/public';
import type { SortOrder } from '@kbn/saved-search-plugin/public';
Expand All @@ -23,9 +23,10 @@ import { SurrDocType } from './services/context';
import { MAX_CONTEXT_SIZE, MIN_CONTEXT_SIZE } from './services/constants';
import { DocTableContext } from '../../components/doc_table/doc_table_context';
import { useDiscoverServices } from '../../hooks/use_discover_services';
import type { DataTableRecord } from '../../types';
import type { DataTableRecord, SearchResponseInterceptedWarning } from '../../types';
import { DiscoverGridFlyout } from '../../components/discover_grid/discover_grid_flyout';
import { DocViewer } from '../../services/doc_views/components/doc_viewer';
import { WarningsCallout } from '../../components/common/warnings_callout';

export interface ContextAppContentProps {
columns: string[];
Expand All @@ -41,6 +42,7 @@ export interface ContextAppContentProps {
anchorStatus: LoadingStatus;
predecessorsStatus: LoadingStatus;
successorsStatus: LoadingStatus;
interceptedWarnings: SearchResponseInterceptedWarning[] | undefined;
useNewFieldsApi: boolean;
isLegacy: boolean;
setAppState: (newState: Partial<AppState>) => void;
Expand Down Expand Up @@ -71,6 +73,7 @@ export function ContextAppContent({
anchorStatus,
predecessorsStatus,
successorsStatus,
interceptedWarnings,
useNewFieldsApi,
isLegacy,
setAppState,
Expand Down Expand Up @@ -118,6 +121,16 @@ export function ContextAppContent({

return (
<Fragment>
{!!interceptedWarnings?.length && (
<>
<WarningsCallout
variant="inline"
interceptedWarnings={interceptedWarnings}
data-test-subj="dscContextInterceptedWarnings"
/>
<EuiSpacer size="s" />
</>
)}
<ActionBarMemoized
type={SurrDocType.PREDECESSORS}
defaultStepSize={defaultStepSize}
Expand Down
Expand Up @@ -19,13 +19,20 @@ import {
mockSuccessorHits,
} from '../__mocks__/use_context_app_fetch';
import { dataViewWithTimefieldMock } from '../../../__mocks__/data_view_with_timefield';
import { searchResponseWarningsMock } from '../../../__mocks__/search_response_warnings';
import { createContextSearchSourceStub } from '../services/_stubs';
import { DataView } from '@kbn/data-views-plugin/public';
import { themeServiceMock } from '@kbn/core/public/mocks';
import { KibanaContextProvider } from '@kbn/kibana-react-plugin/public';

const mockInterceptedWarnings = searchResponseWarningsMock.map((originalWarning) => ({
originalWarning,
}));

const mockFilterManager = createFilterManagerMock();

let mockOverrideInterceptedWarnings = false;

jest.mock('../services/context', () => {
const originalModule = jest.requireActual('../services/context');
return {
Expand All @@ -35,7 +42,12 @@ jest.mock('../services/context', () => {
if (!dataView || !dataView.id) {
throw new Error();
}
return type === 'predecessors' ? mockPredecessorHits : mockSuccessorHits;
return {
rows: type === 'predecessors' ? mockPredecessorHits : mockSuccessorHits,
interceptedWarnings: mockOverrideInterceptedWarnings
? [mockInterceptedWarnings[type === 'predecessors' ? 0 : 1]]
: undefined,
};
},
};
});
Expand All @@ -45,7 +57,12 @@ jest.mock('../services/anchor', () => ({
if (!dataView.id || !anchorId) {
throw new Error();
}
return mockAnchorHit;
return {
anchorRow: mockAnchorHit,
interceptedWarnings: mockOverrideInterceptedWarnings
? [mockInterceptedWarnings[2]]
: undefined,
};
},
}));

Expand Down Expand Up @@ -118,6 +135,9 @@ describe('test useContextAppFetch', () => {
expect(result.current.fetchedState.anchor).toEqual({ ...mockAnchorHit, isAnchor: true });
expect(result.current.fetchedState.predecessors).toEqual(mockPredecessorHits);
expect(result.current.fetchedState.successors).toEqual(mockSuccessorHits);
expect(result.current.fetchedState.predecessorsInterceptedWarnings).toBeUndefined();
expect(result.current.fetchedState.successorsInterceptedWarnings).toBeUndefined();
expect(result.current.fetchedState.anchorInterceptedWarnings).toBeUndefined();
});

it('should set anchorStatus to failed when tieBreakingField array is empty', async () => {
Expand Down Expand Up @@ -187,4 +207,34 @@ describe('test useContextAppFetch', () => {
expect(result.current.fetchedState.predecessors).toEqual([]);
expect(result.current.fetchedState.successors).toEqual([]);
});

it('should handle warnings', async () => {
mockOverrideInterceptedWarnings = true;

const { result } = initDefaults(['_doc']);

expect(result.current.fetchedState.anchorStatus.value).toBe(LoadingStatus.UNINITIALIZED);
expect(result.current.fetchedState.predecessorsStatus.value).toBe(LoadingStatus.UNINITIALIZED);
expect(result.current.fetchedState.successorsStatus.value).toBe(LoadingStatus.UNINITIALIZED);

await act(async () => {
await result.current.fetchAllRows();
});

expect(result.current.fetchedState.anchorStatus.value).toBe(LoadingStatus.LOADED);
expect(result.current.fetchedState.predecessorsStatus.value).toBe(LoadingStatus.LOADED);
expect(result.current.fetchedState.successorsStatus.value).toBe(LoadingStatus.LOADED);
expect(result.current.fetchedState.anchor).toEqual({ ...mockAnchorHit, isAnchor: true });
expect(result.current.fetchedState.predecessors).toEqual(mockPredecessorHits);
expect(result.current.fetchedState.successors).toEqual(mockSuccessorHits);
expect(result.current.fetchedState.predecessorsInterceptedWarnings).toEqual([
mockInterceptedWarnings[0],
]);
expect(result.current.fetchedState.successorsInterceptedWarnings).toEqual([
mockInterceptedWarnings[1],
]);
expect(result.current.fetchedState.anchorInterceptedWarnings).toEqual([
mockInterceptedWarnings[2],
]);
});
});
Expand Up @@ -41,13 +41,8 @@ export function useContextAppFetch({
appState,
useNewFieldsApi,
}: ContextAppFetchProps) {
const {
uiSettings: config,
data,
toastNotifications,
filterManager,
core,
} = useDiscoverServices();
const services = useDiscoverServices();
const { uiSettings: config, data, toastNotifications, filterManager, core } = services;
const { theme$ } = core.theme;

const searchSource = useMemo(() => {
Expand Down Expand Up @@ -95,9 +90,20 @@ export function useContextAppFetch({
{ [dataView.timeFieldName!]: SortDirection.desc },
{ [tieBreakerField]: SortDirection.desc },
];
const anchor = await fetchAnchor(anchorId, dataView, searchSource, sort, useNewFieldsApi);
setState({ anchor, anchorStatus: { value: LoadingStatus.LOADED } });
return anchor;
const result = await fetchAnchor(
anchorId,
dataView,
searchSource,
sort,
useNewFieldsApi,
services
);
setState({
anchor: result.anchorRow,
anchorInterceptedWarnings: result.interceptedWarnings,
anchorStatus: { value: LoadingStatus.LOADED },
});
return result.anchorRow;
} catch (error) {
setState(createError('anchorStatus', FailureReason.UNKNOWN, error));
toastNotifications.addDanger({
Expand All @@ -106,6 +112,7 @@ export function useContextAppFetch({
});
}
}, [
services,
tieBreakerField,
setState,
toastNotifications,
Expand All @@ -124,13 +131,14 @@ export function useContextAppFetch({
type === SurrDocType.PREDECESSORS ? appState.predecessorCount : appState.successorCount;
const anchor = fetchedAnchor || fetchedState.anchor;
const statusKey = `${type}Status`;
const warningsKey = `${type}InterceptedWarnings`;
const errorTitle = i18n.translate('discover.context.unableToLoadDocumentDescription', {
defaultMessage: 'Unable to load documents',
});

try {
setState({ [statusKey]: { value: LoadingStatus.LOADING } });
const rows = anchor.id
const result = anchor.id
? await fetchSurroundingDocs(
type,
dataView,
Expand All @@ -140,10 +148,15 @@ export function useContextAppFetch({
count,
filters,
data,
useNewFieldsApi
useNewFieldsApi,
services
)
: [];
setState({ [type]: rows, [statusKey]: { value: LoadingStatus.LOADED } });
: { rows: [], interceptedWarnings: undefined };
setState({
[type]: result.rows,
[warningsKey]: result.interceptedWarnings,
[statusKey]: { value: LoadingStatus.LOADED },
});
} catch (error) {
setState(createError(statusKey, FailureReason.UNKNOWN, error));
toastNotifications.addDanger({
Expand All @@ -155,6 +168,7 @@ export function useContextAppFetch({
}
},
[
services,
filterManager,
appState,
fetchedState.anchor,
Expand Down