diff --git a/src/plugins/discover/public/__mocks__/index_patterns.ts b/src/plugins/discover/public/__mocks__/index_patterns.ts index 88447eacc884d..b90338e895623 100644 --- a/src/plugins/discover/public/__mocks__/index_patterns.ts +++ b/src/plugins/discover/public/__mocks__/index_patterns.ts @@ -10,12 +10,14 @@ import { IndexPatternsService } from '../../../data/common'; import { indexPatternMock } from './index_pattern'; export const indexPatternsMock = { - getCache: () => { + getCache: async () => { return [indexPatternMock]; }, - get: (id: string) => { + get: async (id: string) => { if (id === 'the-index-pattern-id') { - return indexPatternMock; + return Promise.resolve(indexPatternMock); + } else if (id === 'invalid-index-pattern-id') { + return Promise.reject('Invald'); } }, updateSavedObject: jest.fn(), diff --git a/src/plugins/discover/public/application/apps/context/context_app_route.tsx b/src/plugins/discover/public/application/apps/context/context_app_route.tsx index d124fd6cfa395..6c4722418be14 100644 --- a/src/plugins/discover/public/application/apps/context/context_app_route.tsx +++ b/src/plugins/discover/public/application/apps/context/context_app_route.tsx @@ -8,6 +8,8 @@ import React, { useEffect } from 'react'; import { useParams } from 'react-router-dom'; import { i18n } from '@kbn/i18n'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverServices } from '../../../build_services'; import { ContextApp } from './context_app'; import { getRootBreadcrumbs } from '../../helpers/breadcrumbs'; @@ -43,7 +45,29 @@ export function ContextAppRoute(props: ContextAppProps) { ]); }, [chrome]); - const indexPattern = useIndexPattern(services.indexPatterns, indexPatternId); + const { indexPattern, error } = useIndexPattern(services.indexPatterns, indexPatternId); + + if (error) { + return ( + + } + body={ + + } + /> + ); + } if (!indexPattern) { return ; diff --git a/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx b/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx index 31ff39ea6b577..68c012ddd92e9 100644 --- a/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx +++ b/src/plugins/discover/public/application/apps/doc/components/doc.test.tsx @@ -14,6 +14,7 @@ import { ReactWrapper } from 'enzyme'; import { findTestSubject } from '@elastic/eui/lib/test'; import { Doc, DocProps } from './doc'; import { SEARCH_FIELDS_FROM_SOURCE as mockSearchFieldsFromSource } from '../../../../../common'; +import { indexPatternMock } from '../../../../__mocks__/index_pattern'; const mockSearchApi = jest.fn(); @@ -74,21 +75,11 @@ const waitForPromises = async () => * this works but logs ugly error messages until we're using React 16.9 * should be adapted when we upgrade */ -// eslint-disable-next-line @typescript-eslint/no-explicit-any -async function mountDoc(update = false, indexPatternGetter: any = null) { - const indexPattern = { - getComputedFields: () => [], - }; - const indexPatternService = { - get: indexPatternGetter ? indexPatternGetter : jest.fn(() => Promise.resolve(indexPattern)), - // eslint-disable-next-line @typescript-eslint/no-explicit-any - } as any; - +async function mountDoc(update = false) { const props = { id: '1', index: 'index1', - indexPatternId: 'xyz', - indexPatternService, + indexPattern: indexPatternMock, } as DocProps; let comp!: ReactWrapper; await act(async () => { @@ -108,12 +99,6 @@ describe('Test of of Discover', () => { expect(findTestSubject(comp, 'doc-msg-loading').length).toBe(1); }); - test('renders IndexPattern notFound msg', async () => { - const indexPatternGetter = jest.fn(() => Promise.reject({ savedObjectId: '007' })); - const comp = await mountDoc(true, indexPatternGetter); - expect(findTestSubject(comp, 'doc-msg-notFoundIndexPattern').length).toBe(1); - }); - test('renders notFound msg', async () => { mockSearchApi.mockImplementation(() => throwError({ status: 404 })); const comp = await mountDoc(true); diff --git a/src/plugins/discover/public/application/apps/doc/components/doc.tsx b/src/plugins/discover/public/application/apps/doc/components/doc.tsx index f33ffe561e490..c6cfad3953e95 100644 --- a/src/plugins/discover/public/application/apps/doc/components/doc.tsx +++ b/src/plugins/discover/public/application/apps/doc/components/doc.tsx @@ -9,7 +9,7 @@ import React from 'react'; import { FormattedMessage } from '@kbn/i18n/react'; import { EuiCallOut, EuiLink, EuiLoadingSpinner, EuiPageContent, EuiPage } from '@elastic/eui'; -import { IndexPatternsContract } from 'src/plugins/data/public'; +import { IndexPattern } from 'src/plugins/data/public'; import { getServices } from '../../../../kibana_services'; import { DocViewer } from '../../../components/doc_viewer/doc_viewer'; import { ElasticRequestState } from '../types'; @@ -25,14 +25,9 @@ export interface DocProps { */ index: string; /** - * IndexPattern ID used to get IndexPattern entity - * that's used for adding additional fields (stored_fields, script_fields, docvalue_fields) + * IndexPattern entity */ - indexPatternId: string; - /** - * IndexPatternService to get a given index pattern by ID - */ - indexPatternService: IndexPatternsContract; + indexPattern: IndexPattern; /** * If set, will always request source, regardless of the global `fieldsFromSource` setting */ @@ -40,7 +35,8 @@ export interface DocProps { } export function Doc(props: DocProps) { - const [reqState, hit, indexPattern] = useEsDocSearch(props); + const { indexPattern } = props; + const [reqState, hit] = useEsDocSearch(props); const indexExistsLink = getServices().docLinks.links.apis.indexExists; return ( @@ -54,7 +50,7 @@ export function Doc(props: DocProps) { } /> diff --git a/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx b/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx index 8398f6255e0f9..aef928d523515 100644 --- a/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx +++ b/src/plugins/discover/public/application/apps/doc/single_doc_route.tsx @@ -7,6 +7,8 @@ */ import React, { useEffect } from 'react'; import { useLocation, useParams } from 'react-router-dom'; +import { EuiEmptyPrompt } from '@elastic/eui'; +import { FormattedMessage } from '@kbn/i18n/react'; import { DiscoverServices } from '../../../build_services'; import { getRootBreadcrumbs } from '../../helpers/breadcrumbs'; import { Doc } from './components/doc'; @@ -31,7 +33,7 @@ function useQuery() { export function SingleDocRoute(props: SingleDocRouteProps) { const { services } = props; - const { chrome, timefilter, indexPatterns } = services; + const { chrome, timefilter } = services; const { indexPatternId, index } = useParams(); @@ -52,7 +54,29 @@ export function SingleDocRoute(props: SingleDocRouteProps) { timefilter.disableTimeRangeSelector(); }); - const indexPattern = useIndexPattern(services.indexPatterns, indexPatternId); + const { indexPattern, error } = useIndexPattern(services.indexPatterns, indexPatternId); + + if (error) { + return ( + + } + body={ + + } + /> + ); + } if (!indexPattern) { return ; @@ -60,12 +84,7 @@ export function SingleDocRoute(props: SingleDocRouteProps) { return (
- +
); } diff --git a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx index a2434170acdd7..188deba755445 100644 --- a/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx +++ b/src/plugins/discover/public/application/components/doc_viewer/doc_viewer_tab.test.tsx @@ -10,6 +10,7 @@ import React from 'react'; import { shallow } from 'enzyme'; import { DocViewerTab } from './doc_viewer_tab'; import { ElasticSearchHit } from '../../doc_views/doc_views_types'; +import { indexPatternMock } from '../../../__mocks__/index_pattern'; describe('DocViewerTab', () => { test('changing columns triggers an update', () => { @@ -21,6 +22,7 @@ describe('DocViewerTab', () => { renderProps: { hit: {} as ElasticSearchHit, columns: ['test'], + indexPattern: indexPatternMock, }, }; @@ -31,6 +33,7 @@ describe('DocViewerTab', () => { renderProps: { hit: {} as ElasticSearchHit, columns: ['test2'], + indexPattern: indexPatternMock, }, }; diff --git a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap index 82d9183f3d394..761263ee861b9 100644 --- a/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap +++ b/src/plugins/discover/public/application/components/source_viewer/__snapshots__/source_viewer.test.tsx.snap @@ -5,7 +5,11 @@ exports[`Source Viewer component renders error state 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, @@ -264,7 +268,11 @@ exports[`Source Viewer component renders json code editor 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, @@ -619,7 +627,11 @@ exports[`Source Viewer component renders loading state 1`] = ` hasLineNumbers={true} id="1" index="index1" - indexPatternId="xyz" + indexPattern={ + Object { + "getComputedFields": [Function], + } + } intl={ Object { "defaultFormats": Object {}, diff --git a/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx b/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx index 7895c1025dda9..a98c2de6197d8 100644 --- a/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx +++ b/src/plugins/discover/public/application/components/source_viewer/source_viewer.test.tsx @@ -43,13 +43,13 @@ const mockIndexPatternService = { })); describe('Source Viewer component', () => { test('renders loading state', () => { - jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [0, null, null, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [0, null, () => {}]); const comp = mountWithIntl( @@ -60,13 +60,13 @@ describe('Source Viewer component', () => { }); test('renders error state', () => { - jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [3, null, null, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [3, null, () => {}]); const comp = mountWithIntl( @@ -97,9 +97,7 @@ describe('Source Viewer component', () => { _underscore: 123, }, } as never; - jest - .spyOn(hooks, 'useEsDocSearch') - .mockImplementation(() => [2, mockHit, mockIndexPattern, () => {}]); + jest.spyOn(hooks, 'useEsDocSearch').mockImplementation(() => [2, mockHit, () => {}]); jest.spyOn(useUiSettingHook, 'useUiSetting').mockImplementation(() => { return false; }); @@ -107,7 +105,7 @@ describe('Source Viewer component', () => { diff --git a/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx b/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx index 9e37ae8f8bf93..31d4d866df21e 100644 --- a/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx +++ b/src/plugins/discover/public/application/components/source_viewer/source_viewer.tsx @@ -17,11 +17,12 @@ import { getServices } from '../../../kibana_services'; import { SEARCH_FIELDS_FROM_SOURCE } from '../../../../common'; import { ElasticRequestState } from '../../apps/doc/types'; import { useEsDocSearch } from '../../services/use_es_doc_search'; +import { IndexPattern } from '../../../../../data_views/common'; interface SourceViewerProps { id: string; index: string; - indexPatternId: string; + indexPattern: IndexPattern; hasLineNumbers: boolean; width?: number; } @@ -29,19 +30,17 @@ interface SourceViewerProps { export const SourceViewer = ({ id, index, - indexPatternId, + indexPattern, width, hasLineNumbers, }: SourceViewerProps) => { const [editor, setEditor] = useState(); const [jsonValue, setJsonValue] = useState(''); - const indexPatternService = getServices().data.indexPatterns; const useNewFieldsApi = !getServices().uiSettings.get(SEARCH_FIELDS_FROM_SOURCE); - const [reqState, hit, , requestData] = useEsDocSearch({ + const [reqState, hit, requestData] = useEsDocSearch({ id, index, - indexPatternId, - indexPatternService, + indexPattern, requestSource: useNewFieldsApi, }); @@ -106,11 +105,7 @@ export const SourceViewer = ({ ); - if ( - reqState === ElasticRequestState.Error || - reqState === ElasticRequestState.NotFound || - reqState === ElasticRequestState.NotFoundIndexPattern - ) { + if (reqState === ElasticRequestState.Error || reqState === ElasticRequestState.NotFound) { return errorState; } diff --git a/src/plugins/discover/public/application/components/table/table.tsx b/src/plugins/discover/public/application/components/table/table.tsx index 7f597d846f88f..e64dbd10f7855 100644 --- a/src/plugins/discover/public/application/components/table/table.tsx +++ b/src/plugins/discover/public/application/components/table/table.tsx @@ -25,7 +25,7 @@ export interface DocViewerTableProps { columns?: string[]; filter?: DocViewFilterFn; hit: ElasticSearchHit; - indexPattern?: IndexPattern; + indexPattern: IndexPattern; onAddColumn?: (columnName: string) => void; onRemoveColumn?: (columnName: string) => void; } diff --git a/src/plugins/discover/public/application/doc_views/doc_views_types.ts b/src/plugins/discover/public/application/doc_views/doc_views_types.ts index 48bebec22b9b5..d3e482c0f2e1d 100644 --- a/src/plugins/discover/public/application/doc_views/doc_views_types.ts +++ b/src/plugins/discover/public/application/doc_views/doc_views_types.ts @@ -32,7 +32,7 @@ export interface DocViewRenderProps { columns?: string[]; filter?: DocViewFilterFn; hit: ElasticSearchHit; - indexPattern?: IndexPattern; + indexPattern: IndexPattern; onAddColumn?: (columnName: string) => void; onRemoveColumn?: (columnName: string) => void; } diff --git a/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx b/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx index 85282afb6fc37..dfc54d8630742 100644 --- a/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx +++ b/src/plugins/discover/public/application/helpers/use_index_pattern.test.tsx @@ -8,12 +8,24 @@ import { useIndexPattern } from './use_index_pattern'; import { indexPatternMock } from '../../__mocks__/index_pattern'; import { indexPatternsMock } from '../../__mocks__/index_patterns'; -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; describe('Use Index Pattern', () => { test('returning a valid index pattern', async () => { - const { result } = renderHook(() => useIndexPattern(indexPatternsMock, 'the-index-pattern-id')); - await act(() => Promise.resolve()); - expect(result.current).toBe(indexPatternMock); + const { result, waitForNextUpdate } = renderHook(() => + useIndexPattern(indexPatternsMock, 'the-index-pattern-id') + ); + await waitForNextUpdate(); + expect(result.current.indexPattern).toBe(indexPatternMock); + expect(result.current.error).toBe(undefined); + }); + + test('returning an error', async () => { + const { result, waitForNextUpdate } = renderHook(() => + useIndexPattern(indexPatternsMock, 'invalid-index-pattern-id') + ); + await waitForNextUpdate(); + expect(result.current.indexPattern).toBe(undefined); + expect(result.current.error).toBeTruthy(); }); }); diff --git a/src/plugins/discover/public/application/helpers/use_index_pattern.tsx b/src/plugins/discover/public/application/helpers/use_index_pattern.tsx index f53d131920c5c..374f83cbbfe72 100644 --- a/src/plugins/discover/public/application/helpers/use_index_pattern.tsx +++ b/src/plugins/discover/public/application/helpers/use_index_pattern.tsx @@ -10,13 +10,18 @@ import { IndexPattern, IndexPatternsContract } from '../../../../data/common'; export const useIndexPattern = (indexPatterns: IndexPatternsContract, indexPatternId: string) => { const [indexPattern, setIndexPattern] = useState(undefined); + const [error, setError] = useState(); useEffect(() => { async function loadIndexPattern() { - const ip = await indexPatterns.get(indexPatternId); - setIndexPattern(ip); + try { + const item = await indexPatterns.get(indexPatternId); + setIndexPattern(item); + } catch (e) { + setError(e); + } } loadIndexPattern(); - }); - return indexPattern; + }, [indexPatternId, indexPatterns]); + return { indexPattern, error }; }; diff --git a/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx b/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx index af7d189e62882..ca57b470b471a 100644 --- a/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx +++ b/src/plugins/discover/public/application/services/use_es_doc_search.test.tsx @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import { renderHook, act } from '@testing-library/react-hooks'; +import { renderHook } from '@testing-library/react-hooks'; import { buildSearchBody, useEsDocSearch } from './use_es_doc_search'; import { Observable } from 'rxjs'; import { IndexPattern } from 'src/plugins/data/common'; @@ -175,26 +175,14 @@ describe('Test of helper / hook', () => { const indexPattern = { getComputedFields: () => [], }; - const getMock = jest.fn(() => Promise.resolve(indexPattern)); - const indexPatternService = { - get: getMock, - } as unknown as IndexPattern; const props = { id: '1', index: 'index1', - indexPatternId: 'xyz', - indexPatternService, - } as unknown as DocProps; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - let hook: any; - await act(async () => { - hook = renderHook((p: DocProps) => useEsDocSearch(p), { initialProps: props }); - }); - expect(hook.result.current.slice(0, 3)).toEqual([ - ElasticRequestState.Loading, - null, indexPattern, - ]); - expect(getMock).toHaveBeenCalled(); + } as unknown as DocProps; + + const { result } = renderHook((p: DocProps) => useEsDocSearch(p), { initialProps: props }); + + expect(result.current.slice(0, 2)).toEqual([ElasticRequestState.Loading, null]); }); }); diff --git a/src/plugins/discover/public/application/services/use_es_doc_search.ts b/src/plugins/discover/public/application/services/use_es_doc_search.ts index a2f0cd6f8442b..16a24ff27292b 100644 --- a/src/plugins/discover/public/application/services/use_es_doc_search.ts +++ b/src/plugins/discover/public/application/services/use_es_doc_search.ts @@ -64,11 +64,9 @@ export function buildSearchBody( export function useEsDocSearch({ id, index, - indexPatternId, - indexPatternService, + indexPattern, requestSource, -}: DocProps): [ElasticRequestState, ElasticSearchHit | null, IndexPattern | null, () => void] { - const [indexPattern, setIndexPattern] = useState(null); +}: DocProps): [ElasticRequestState, ElasticSearchHit | null | null, () => void] { const [status, setStatus] = useState(ElasticRequestState.Loading); const [hit, setHit] = useState(null); const { data, uiSettings } = useMemo(() => getServices(), []); @@ -76,14 +74,11 @@ export function useEsDocSearch({ const requestData = useCallback(async () => { try { - const indexPatternEntity = await indexPatternService.get(indexPatternId); - setIndexPattern(indexPatternEntity); - const { rawResponse } = await data.search .search({ params: { index, - body: buildSearchBody(id, indexPatternEntity, useNewFieldsApi, requestSource)?.body, + body: buildSearchBody(id, indexPattern, useNewFieldsApi, requestSource)?.body, }, }) .toPromise(); @@ -105,11 +100,11 @@ export function useEsDocSearch({ setStatus(ElasticRequestState.Error); } } - }, [id, index, indexPatternId, indexPatternService, data.search, useNewFieldsApi, requestSource]); + }, [id, index, indexPattern, data.search, useNewFieldsApi, requestSource]); useEffect(() => { requestData(); }, [requestData]); - return [status, hit, indexPattern, requestData]; + return [status, hit, requestData]; } diff --git a/src/plugins/discover/public/plugin.tsx b/src/plugins/discover/public/plugin.tsx index d86e5f363630c..6d30e6fd9e8a9 100644 --- a/src/plugins/discover/public/plugin.tsx +++ b/src/plugins/discover/public/plugin.tsx @@ -267,7 +267,7 @@ export class DiscoverPlugin