Skip to content

Commit

Permalink
[search source] consolidate warnings logic in kbn-search-response-war…
Browse files Browse the repository at this point in the history
…nings package (elastic#168531)

prerequisite for elastic#167906

PR consolidates all warnings logic into kbn-search-response-warnings
package

---------

Co-authored-by: kibanamachine <[email protected]>
  • Loading branch information
nreese and kibanamachine authored Oct 23, 2023
1 parent 699b30d commit b5aa375
Show file tree
Hide file tree
Showing 46 changed files with 350 additions and 458 deletions.
2 changes: 1 addition & 1 deletion examples/search_examples/public/search/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ import {
IKibanaSearchResponse,
isRunningResponse,
} from '@kbn/data-plugin/public';
import { SearchResponseWarning } from '@kbn/data-plugin/public/search/types';
import type { SearchResponseWarning } from '@kbn/search-response-warnings';
import type { DataView, DataViewField } from '@kbn/data-views-plugin/public';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
Expand Down
1 change: 1 addition & 0 deletions examples/search_examples/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,6 @@
"@kbn/core-mount-utils-browser-internal",
"@kbn/config-schema",
"@kbn/shared-ux-router",
"@kbn/search-response-warnings",
]
}
7 changes: 4 additions & 3 deletions packages/kbn-search-response-warnings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,13 @@
* Side Public License, v 1.
*/

export type { SearchResponseInterceptedWarning } from './src/types';
export type { SearchResponseWarning, WarningHandlerCallback } from './src/types';

export {
SearchResponseWarnings,
type SearchResponseWarningsProps,
} from './src/components/search_response_warnings';
export { ViewWarningButton } from './src/components/view_warning_button';

export { getSearchResponseInterceptedWarnings } from './src/utils/get_search_response_intercepted_warnings';
export { hasUnsupportedDownsampledAggregationFailure } from './src/utils/has_unsupported_downsampled_aggregation_failure';
export { handleWarnings } from './src/handle_warnings';
export { hasUnsupportedDownsampledAggregationFailure } from './src/has_unsupported_downsampled_aggregation_failure';
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* Side Public License, v 1.
*/

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

export const searchResponseIncompleteWarningLocalCluster: SearchResponseWarning = {
type: 'incomplete',
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,7 @@ import { mountWithIntl } from '@kbn/test-jest-helpers';
import { SearchResponseWarnings } from './search_response_warnings';
import { searchResponseIncompleteWarningLocalCluster } from '../../__mocks__/search_response_warnings';

const interceptedWarnings = [
{
originalWarning: searchResponseIncompleteWarningLocalCluster,
action: <button>{`test`}</button>,
},
];
const interceptedWarnings = [searchResponseIncompleteWarningLocalCluster];

describe('SearchResponseWarnings', () => {
it('renders "callout" correctly', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,16 +25,17 @@ import {
} from '@elastic/eui';
import { css } from '@emotion/react';
import { i18n } from '@kbn/i18n';
import type { SearchResponseInterceptedWarning } from '../../types';
import { ViewWarningButton } from '../view_warning_button';
import type { SearchResponseWarning } from '../../types';

/**
* SearchResponseWarnings component props
*/
export interface SearchResponseWarningsProps {
/**
* An array of warnings which can have actions
* An array of warnings
*/
interceptedWarnings?: SearchResponseInterceptedWarning[];
interceptedWarnings?: SearchResponseWarning[];

/**
* View variant
Expand Down Expand Up @@ -260,12 +261,12 @@ export const SearchResponseWarnings = ({
};

function WarningContent({
warning: { originalWarning, action },
warning,
textSize = 's',
groupStyles,
'data-test-subj': dataTestSubj,
}: {
warning: SearchResponseInterceptedWarning;
warning: SearchResponseWarning;
textSize?: EuiTextProps['size'];
groupStyles?: Partial<EuiFlexGroupProps>;
'data-test-subj': string;
Expand All @@ -274,10 +275,17 @@ function WarningContent({
<EuiFlexGroup gutterSize="xs" {...groupStyles} wrap>
<EuiFlexItem grow={false}>
<EuiText size={textSize} data-test-subj={`${dataTestSubj}_warningTitle`}>
{originalWarning.message}
{warning.message}
</EuiText>
</EuiFlexItem>
{action ? <EuiFlexItem grow={false}>{action}</EuiFlexItem> : null}
<EuiFlexItem grow={false}>
<ViewWarningButton
color="primary"
size="s"
onClick={warning.openInInspector}
isButtonEmpty={true}
/>
</EuiFlexItem>
</EuiFlexGroup>
);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export default function ViewWarningButton({
return (
<Component color={color} size={size} onClick={onClick} data-test-subj="viewWarningBtn">
<FormattedMessage
id="data.search.searchSource.warning.viewDetailsButtonLabel"
id="searchResponseWarnings.viewDetailsButtonLabel"
defaultMessage="View details"
description="View warning details button label"
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@

import { estypes } from '@elastic/elasticsearch';
import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public';
import type { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request';
import { extractWarnings } from './extract_warnings';

const mockInspectorService = {} as InspectorStartContract;
const mockRequestAdapter = {} as RequestAdapter;

describe('extract search response warnings', () => {
describe('single cluster', () => {
Expand Down Expand Up @@ -40,7 +42,7 @@ describe('extract search response warnings', () => {
aggregations: {},
};

expect(extractWarnings(response, mockInspectorService)).toEqual([
expect(extractWarnings(response, mockInspectorService, mockRequestAdapter)).toEqual([
{
type: 'incomplete',
message: 'Results are partial and may be incomplete.',
Expand All @@ -66,7 +68,7 @@ describe('extract search response warnings', () => {
_shards: {} as estypes.ShardStatistics,
hits: { hits: [] },
};
expect(extractWarnings(response, mockInspectorService)).toEqual([
expect(extractWarnings(response, mockInspectorService, mockRequestAdapter)).toEqual([
{
type: 'incomplete',
message: 'Results are partial and may be incomplete.',
Expand Down Expand Up @@ -94,7 +96,8 @@ describe('extract search response warnings', () => {
total: 9000,
},
} as estypes.SearchResponse,
mockInspectorService
mockInspectorService,
mockRequestAdapter
);

expect(warnings).toEqual([]);
Expand Down Expand Up @@ -185,7 +188,7 @@ describe('extract search response warnings', () => {
aggregations: {},
};

expect(extractWarnings(response, mockInspectorService)).toEqual([
expect(extractWarnings(response, mockInspectorService, mockRequestAdapter)).toEqual([
{
type: 'incomplete',
message: 'Results are partial and may be incomplete.',
Expand Down Expand Up @@ -239,7 +242,7 @@ describe('extract search response warnings', () => {
},
hits: { hits: [] },
};
expect(extractWarnings(response, mockInspectorService)).toEqual([
expect(extractWarnings(response, mockInspectorService, mockRequestAdapter)).toEqual([
{
type: 'incomplete',
message: 'Results are partial and may be incomplete.',
Expand Down Expand Up @@ -293,7 +296,8 @@ describe('extract search response warnings', () => {
},
hits: { hits: [] },
} as estypes.SearchResponse,
mockInspectorService
mockInspectorService,
mockRequestAdapter
);

expect(warnings).toEqual([]);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,17 @@
import { estypes } from '@elastic/elasticsearch';
import { i18n } from '@kbn/i18n';
import type { ClusterDetails } from '@kbn/es-types';
import type { Start as InspectorStartContract } from '@kbn/inspector-plugin/public';
import { RequestAdapter } from '@kbn/inspector-plugin/common/adapters/request';
import type { IInspectorInfo } from '../../../common/search/search_source';
import { SearchResponseWarning } from '../types';
import type { Start as InspectorStartContract, RequestAdapter } from '@kbn/inspector-plugin/public';
import type { SearchResponseWarning } from './types';

/**
* @internal
*/
export function extractWarnings(
rawResponse: estypes.SearchResponse,
inspectorService: InspectorStartContract,
inspector?: IInspectorInfo
requestAdapter: RequestAdapter,
requestId?: string
): SearchResponseWarning[] {
const warnings: SearchResponseWarning[] = [];

Expand All @@ -36,7 +35,7 @@ export function extractWarnings(
if (isPartial) {
warnings.push({
type: 'incomplete',
message: i18n.translate('data.search.searchSource.fetch.incompleteResultsMessage', {
message: i18n.translate('searchResponseWarnings.incompleteResultsMessage', {
defaultMessage: 'Results are partial and may be incomplete.',
}),
clusters: rawResponse._clusters
Expand All @@ -56,23 +55,13 @@ export function extractWarnings(
},
},
openInInspector: () => {
const adapter = inspector?.adapter ? inspector.adapter : new RequestAdapter();
if (!inspector?.adapter) {
const requestResponder = adapter.start(
i18n.translate('data.search.searchSource.anonymousRequestTitle', {
defaultMessage: 'Request',
})
);
requestResponder.ok({ json: rawResponse });
}

inspectorService.open(
{
requests: adapter,
requests: requestAdapter,
},
{
options: {
initialRequestId: inspector?.id,
initialRequestId: requestId,
initialTabs: ['clusters', 'response'],
},
}
Expand Down
117 changes: 117 additions & 0 deletions packages/kbn-search-response-warnings/src/handle_warnings.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
/*
* 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 { estypes } from '@elastic/elasticsearch';
import type { ThemeServiceStart } from '@kbn/core/public';
import type { I18nStart } from '@kbn/core-i18n-browser';
import { notificationServiceMock } from '@kbn/core-notifications-browser-mocks';
import type { Start as InspectorStart, RequestAdapter } from '@kbn/inspector-plugin/public';
import { handleWarnings } from './handle_warnings';

describe('handleWarnings', () => {
const notifications = notificationServiceMock.createStartContract();

beforeEach(() => {
notifications.toasts.addWarning.mockClear();
});

it('should not show notifications when there are no warnings', () => {
handleWarnings({
request: {} as unknown as estypes.SearchRequest,
requestAdapter: {} as unknown as RequestAdapter,
requestId: '1234',
response: {
timed_out: false,
_shards: {
failed: 0,
total: 9000,
},
} as estypes.SearchResponse,
services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart,
notifications,
theme: {} as unknown as ThemeServiceStart,
},
});

expect(notifications.toasts.addWarning).toBeCalledTimes(0);
});

it('should show notifications for warnings when there is no callback', () => {
handleWarnings({
request: {} as unknown as estypes.SearchRequest,
requestAdapter: {} as unknown as RequestAdapter,
requestId: '1234',
response: {
took: 999,
timed_out: true,
_shards: {} as estypes.ShardStatistics,
hits: { hits: [] },
} as estypes.SearchResponse,
services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart,
notifications,
theme: {} as unknown as ThemeServiceStart,
},
});

expect(notifications.toasts.addWarning).toBeCalledTimes(1);
});

it('should show notifications for warnings not handled by callback', () => {
const callbackMock = jest.fn(() => false);
handleWarnings({
callback: callbackMock,
request: {} as unknown as estypes.SearchRequest,
requestAdapter: {} as unknown as RequestAdapter,
requestId: '1234',
response: {
took: 999,
timed_out: true,
_shards: {} as estypes.ShardStatistics,
hits: { hits: [] },
} as estypes.SearchResponse,
services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart,
notifications,
theme: {} as unknown as ThemeServiceStart,
},
});

expect(callbackMock).toBeCalledTimes(1);
expect(notifications.toasts.addWarning).toBeCalledTimes(1);
});

it('should not show notifications for warnings handled by callback', () => {
const callbackMock = jest.fn(() => true);
handleWarnings({
callback: callbackMock,
request: {} as unknown as estypes.SearchRequest,
requestAdapter: {} as unknown as RequestAdapter,
requestId: '1234',
response: {
took: 999,
timed_out: true,
_shards: {} as estypes.ShardStatistics,
hits: { hits: [] },
} as estypes.SearchResponse,
services: {
i18n: {} as unknown as I18nStart,
inspector: {} as unknown as InspectorStart,
notifications,
theme: {} as unknown as ThemeServiceStart,
},
});

expect(callbackMock).toBeCalledTimes(1);
expect(notifications.toasts.addWarning).toBeCalledTimes(0);
});
});
Loading

0 comments on commit b5aa375

Please sign in to comment.