diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_states.scss b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.scss similarity index 100% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_states.scss rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.scss diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_states.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx similarity index 55% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_states.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx index 25a9fa7430c40..7e6876bc9b3a4 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_states.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.test.tsx @@ -4,28 +4,19 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; +import '../../../../__mocks__/shallow_usecontext.mock'; import React from 'react'; import { shallow } from 'enzyme'; -import { EuiEmptyPrompt, EuiButton, EuiLoadingContent } from '@elastic/eui'; -import { ErrorStatePrompt } from '../../../shared/error_state'; +import { EuiEmptyPrompt, EuiButton } from '@elastic/eui'; -jest.mock('../../../shared/telemetry', () => ({ +jest.mock('../../../../shared/telemetry', () => ({ sendTelemetry: jest.fn(), SendAppSearchTelemetry: jest.fn(), })); -import { sendTelemetry } from '../../../shared/telemetry'; +import { sendTelemetry } from '../../../../shared/telemetry'; -import { ErrorState, EmptyState, LoadingState } from './'; - -describe('ErrorState', () => { - it('renders', () => { - const wrapper = shallow(); - - expect(wrapper.find(ErrorStatePrompt)).toHaveLength(1); - }); -}); +import { EmptyState } from './'; describe('EmptyState', () => { it('renders', () => { @@ -44,11 +35,3 @@ describe('EmptyState', () => { (sendTelemetry as jest.Mock).mockClear(); }); }); - -describe('LoadingState', () => { - it('renders', () => { - const wrapper = shallow(); - - expect(wrapper.find(EuiLoadingContent)).toHaveLength(2); - }); -}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx similarity index 84% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx index 9b0edb423bc52..58691cf09b4a5 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/empty_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/empty_state.tsx @@ -8,14 +8,14 @@ import React, { useContext } from 'react'; import { EuiPageContent, EuiEmptyPrompt, EuiButton } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { sendTelemetry } from '../../../shared/telemetry'; -import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; -import { KibanaContext, IKibanaContext } from '../../../index'; -import { CREATE_ENGINES_PATH } from '../../routes'; +import { sendTelemetry } from '../../../../shared/telemetry'; +import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { KibanaContext, IKibanaContext } from '../../../../index'; +import { CREATE_ENGINES_PATH } from '../../../routes'; -import { EngineOverviewHeader } from '../engine_overview_header'; +import { EngineOverviewHeader } from './header'; -import './empty_states.scss'; +import './empty_state.scss'; export const EmptyState: React.FC = () => { const { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx similarity index 77% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx index 7d2106f2a56f7..7f22ce132d405 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.test.tsx @@ -4,15 +4,15 @@ * you may not use this file except in compliance with the Elastic License. */ -import '../../../__mocks__/shallow_usecontext.mock'; +import '../../../../__mocks__/shallow_usecontext.mock'; import React from 'react'; import { shallow } from 'enzyme'; -jest.mock('../../../shared/telemetry', () => ({ sendTelemetry: jest.fn() })); -import { sendTelemetry } from '../../../shared/telemetry'; +jest.mock('../../../../shared/telemetry', () => ({ sendTelemetry: jest.fn() })); +import { sendTelemetry } from '../../../../shared/telemetry'; -import { EngineOverviewHeader } from '../engine_overview_header'; +import { EngineOverviewHeader } from './'; describe('EngineOverviewHeader', () => { it('renders', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx similarity index 92% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx index 7f67d00f5df91..1a1ae295d4828 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/engine_overview_header.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/header.tsx @@ -15,8 +15,8 @@ import { } from '@elastic/eui'; import { FormattedMessage } from '@kbn/i18n/react'; -import { sendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; +import { sendTelemetry } from '../../../../shared/telemetry'; +import { KibanaContext, IKibanaContext } from '../../../../index'; export const EngineOverviewHeader: React.FC = () => { const { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts similarity index 87% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/index.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts index e92bf214c4cc7..794053f184f8c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/index.ts @@ -4,6 +4,6 @@ * you may not use this file except in compliance with the Elastic License. */ +export { EngineOverviewHeader } from './header'; export { LoadingState } from './loading_state'; export { EmptyState } from './empty_state'; -export { ErrorState } from './error_state'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.test.tsx new file mode 100644 index 0000000000000..c894500550a0b --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.test.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; +import { EuiLoadingContent } from '@elastic/eui'; + +import { LoadingState } from './'; + +describe('LoadingState', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(EuiLoadingContent)).toHaveLength(2); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.tsx similarity index 73% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.tsx index 221091b79dc54..07643560df3c7 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/loading_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/components/loading_state.tsx @@ -7,17 +7,15 @@ import React from 'react'; import { EuiPageContent, EuiSpacer, EuiLoadingContent } from '@elastic/eui'; -import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; -import { EngineOverviewHeader } from '../engine_overview_header'; - -import './empty_states.scss'; +import { SetAppSearchChrome as SetPageChrome } from '../../../../shared/kibana_chrome'; +import { EngineOverviewHeader } from './header'; export const LoadingState: React.FC = () => { return ( <> - + diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx index 45ab5dc5b9ab1..c2379fb33bd71 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.test.tsx @@ -12,7 +12,7 @@ import { shallow, ReactWrapper } from 'enzyme'; import { mountWithAsyncContext, mockKibanaContext } from '../../../__mocks__'; -import { LoadingState, EmptyState, ErrorState } from '../empty_states'; +import { LoadingState, EmptyState } from './components'; import { EngineTable } from './engine_table'; import { EngineOverview } from './'; @@ -40,16 +40,6 @@ describe('EngineOverview', () => { expect(wrapper.find(EmptyState)).toHaveLength(1); }); - - it('hasErrorConnecting', async () => { - const wrapper = await mountWithAsyncContext(, { - http: { - ...mockHttp, - get: () => ({ invalidPayload: true }), - }, - }); - expect(wrapper.find(ErrorState)).toHaveLength(1); - }); }); describe('happy-path states', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx index acac5d17665b7..74bcd9aeafb28 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_overview.tsx @@ -22,8 +22,7 @@ import { KibanaContext, IKibanaContext } from '../../../index'; import EnginesIcon from '../../assets/engine.svg'; import MetaEnginesIcon from '../../assets/meta_engine.svg'; -import { LoadingState, EmptyState, ErrorState } from '../empty_states'; -import { EngineOverviewHeader } from '../engine_overview_header'; +import { EngineOverviewHeader, LoadingState, EmptyState } from './components'; import { EngineTable } from './engine_table'; import './engine_overview.scss'; @@ -42,8 +41,6 @@ export const EngineOverview: React.FC = () => { const { license } = useContext(LicenseContext) as ILicenseContext; const [isLoading, setIsLoading] = useState(true); - const [hasErrorConnecting, setHasErrorConnecting] = useState(false); - const [engines, setEngines] = useState([]); const [enginesPage, setEnginesPage] = useState(1); const [enginesTotal, setEnginesTotal] = useState(0); @@ -57,16 +54,12 @@ export const EngineOverview: React.FC = () => { }); }; const setEnginesData = async (params: IGetEnginesParams, callbacks: ISetEnginesCallbacks) => { - try { - const response = await getEnginesData(params); + const response = await getEnginesData(params); - callbacks.setResults(response.results); - callbacks.setResultsTotal(response.meta.page.total_results); + callbacks.setResults(response.results); + callbacks.setResultsTotal(response.meta.page.total_results); - setIsLoading(false); - } catch (error) { - setHasErrorConnecting(true); - } + setIsLoading(false); }; useEffect(() => { @@ -85,7 +78,6 @@ export const EngineOverview: React.FC = () => { } }, [license, metaEnginesPage]); - if (hasErrorConnecting) return ; if (isLoading) return ; if (!engines.length) return ; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.test.tsx new file mode 100644 index 0000000000000..8d48875a8e1f5 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.test.tsx @@ -0,0 +1,19 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React from 'react'; +import { shallow } from 'enzyme'; + +import { ErrorStatePrompt } from '../../../shared/error_state'; +import { ErrorConnecting } from './'; + +describe('ErrorConnecting', () => { + it('renders', () => { + const wrapper = shallow(); + + expect(wrapper.find(ErrorStatePrompt)).toHaveLength(1); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx similarity index 81% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx index c5a5f1fbb921f..34eb76d11a663 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/error_state.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/error_connecting.tsx @@ -10,17 +10,13 @@ import { EuiPageContent } from '@elastic/eui'; import { ErrorStatePrompt } from '../../../shared/error_state'; import { SetAppSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; -import { EngineOverviewHeader } from '../engine_overview_header'; -import './empty_states.scss'; - -export const ErrorState: React.FC = () => { +export const ErrorConnecting: React.FC = () => { return ( <> - diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/index.ts b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/index.ts similarity index 78% rename from x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/index.ts rename to x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/index.ts index 2d37f037e21e5..c8b71e1a6e791 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview_header/index.ts +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/error_connecting/index.ts @@ -4,4 +4,4 @@ * you may not use this file except in compliance with the Elastic License. */ -export { EngineOverviewHeader } from './engine_overview_header'; +export { ErrorConnecting } from './error_connecting'; diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx index 0f4072c591bc7..94e9127bbed74 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.test.tsx @@ -12,8 +12,10 @@ import { Redirect } from 'react-router-dom'; import { shallow } from 'enzyme'; import { useValues, useActions } from 'kea'; -import { SetupGuide } from './components/setup_guide'; import { Layout, SideNav, SideNavLink } from '../shared/layout'; +import { SetupGuide } from './components/setup_guide'; +import { ErrorConnecting } from './components/error_connecting'; +import { EngineOverview } from './components/engine_overview'; import { AppSearch, AppSearchUnconfigured, AppSearchConfigured, AppSearchNav } from './'; describe('AppSearch', () => { @@ -42,12 +44,17 @@ describe('AppSearchUnconfigured', () => { }); describe('AppSearchConfigured', () => { - it('renders with layout', () => { + beforeEach(() => { + // Mock resets + (useValues as jest.Mock).mockImplementation(() => ({})); (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData: () => {} })); + }); + it('renders with layout', () => { const wrapper = shallow(); expect(wrapper.find(Layout)).toHaveLength(1); + expect(wrapper.find(EngineOverview)).toHaveLength(1); }); it('initializes app data with passed props', () => { @@ -62,12 +69,20 @@ describe('AppSearchConfigured', () => { it('does not re-initialize app data', () => { const initializeAppData = jest.fn(); (useActions as jest.Mock).mockImplementation(() => ({ initializeAppData })); - (useValues as jest.Mock).mockImplementationOnce(() => ({ hasInitialized: true })); + (useValues as jest.Mock).mockImplementation(() => ({ hasInitialized: true })); shallow(); expect(initializeAppData).not.toHaveBeenCalled(); }); + + it('renders ErrorConnecting', () => { + (useValues as jest.Mock).mockImplementation(() => ({ errorConnecting: true })); + + const wrapper = shallow(); + + expect(wrapper.find(ErrorConnecting)).toHaveLength(1); + }); }); describe('AppSearchNav', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx index 5f4734630624c..234201a157ec9 100644 --- a/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/app_search/index.tsx @@ -11,6 +11,7 @@ import { useActions, useValues } from 'kea'; import { i18n } from '@kbn/i18n'; import { KibanaContext, IKibanaContext } from '../index'; +import { HttpLogic, IHttpLogicValues } from '../shared/http'; import { AppLogic, IAppLogicActions, IAppLogicValues } from './app_logic'; import { IInitialAppData } from '../../../common/types'; @@ -27,6 +28,7 @@ import { } from './routes'; import { SetupGuide } from './components/setup_guide'; +import { ErrorConnecting } from './components/error_connecting'; import { EngineOverview } from './components/engine_overview'; export const AppSearch: React.FC = (props) => { @@ -48,6 +50,7 @@ export const AppSearchUnconfigured: React.FC = () => ( export const AppSearchConfigured: React.FC = (props) => { const { hasInitialized } = useValues(AppLogic) as IAppLogicValues; const { initializeAppData } = useActions(AppLogic) as IAppLogicActions; + const { errorConnecting } = useValues(HttpLogic) as IHttpLogicValues; useEffect(() => { if (!hasInitialized) initializeAppData(props); @@ -60,14 +63,18 @@ export const AppSearchConfigured: React.FC = (props) => { }> - - - - - - - - + {errorConnecting ? ( + + ) : ( + + + + + + + + + )} diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx index d6cc6e81509b2..60e4cedf413f2 100644 --- a/x-pack/plugins/enterprise_search/public/applications/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx @@ -22,6 +22,7 @@ import { } from 'src/core/public'; import { ClientConfigType, ClientData, PluginsSetup } from '../plugin'; import { LicenseProvider } from './shared/licensing'; +import { HttpProvider } from './shared/http'; import { IExternalUrl } from './shared/enterprise_search_url'; import { IInitialAppData } from '../../common/types'; @@ -48,7 +49,7 @@ export const renderApp = ( core: CoreStart, plugins: PluginsSetup, config: ClientConfigType, - { externalUrl, ...initialData }: ClientData + { externalUrl, errorConnecting, ...initialData }: ClientData ) => { resetContext({ createStore: true }); const store = getContext().store as Store; @@ -67,6 +68,7 @@ export const renderApp = ( > + @@ -77,7 +79,6 @@ export const renderApp = ( params.element ); return () => { - resetContext({}); ReactDOM.unmountComponentAtNode(params.element); }; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts new file mode 100644 index 0000000000000..a6957340d33d3 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.test.ts @@ -0,0 +1,110 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { resetContext } from 'kea'; + +import { httpServiceMock } from 'src/core/public/mocks'; + +import { HttpLogic } from './http_logic'; + +describe('HttpLogic', () => { + const mockHttp = httpServiceMock.createSetupContract(); + const DEFAULT_VALUES = { + http: null, + httpInterceptors: [], + errorConnecting: false, + }; + + beforeEach(() => { + jest.clearAllMocks(); + resetContext({}); + }); + + it('has expected default values', () => { + HttpLogic.mount(); + expect(HttpLogic.values).toEqual(DEFAULT_VALUES); + }); + + describe('initializeHttp()', () => { + it('sets values based on passed props', () => { + HttpLogic.mount(); + HttpLogic.actions.initializeHttp({ http: mockHttp, errorConnecting: true }); + + expect(HttpLogic.values).toEqual({ + http: mockHttp, + httpInterceptors: [], + errorConnecting: true, + }); + }); + }); + + describe('setErrorConnecting()', () => { + it('sets errorConnecting value', () => { + HttpLogic.mount(); + HttpLogic.actions.setErrorConnecting(true); + expect(HttpLogic.values.errorConnecting).toEqual(true); + + HttpLogic.actions.setErrorConnecting(false); + expect(HttpLogic.values.errorConnecting).toEqual(false); + }); + }); + + describe('http interceptors', () => { + describe('initializeHttpInterceptors()', () => { + beforeEach(() => { + HttpLogic.mount(); + jest.spyOn(HttpLogic.actions, 'setHttpInterceptors'); + jest.spyOn(HttpLogic.actions, 'setErrorConnecting'); + HttpLogic.actions.initializeHttp({ http: mockHttp }); + + HttpLogic.actions.initializeHttpInterceptors(); + }); + + it('calls http.intercept and sets an array of interceptors', () => { + mockHttp.intercept.mockImplementationOnce(() => 'removeInterceptorFn' as any); + HttpLogic.actions.initializeHttpInterceptors(); + + expect(mockHttp.intercept).toHaveBeenCalled(); + expect(HttpLogic.actions.setHttpInterceptors).toHaveBeenCalledWith(['removeInterceptorFn']); + }); + + describe('errorConnectingInterceptor', () => { + it('handles errors connecting to Enterprise Search', async () => { + const { responseError } = mockHttp.intercept.mock.calls[0][0] as any; + await responseError({ response: { url: '/api/app_search/engines', status: 502 } }); + + expect(HttpLogic.actions.setErrorConnecting).toHaveBeenCalled(); + }); + + it('does not handle non-502 Enterprise Search errors', async () => { + const { responseError } = mockHttp.intercept.mock.calls[0][0] as any; + await responseError({ response: { url: '/api/workplace_search/overview', status: 404 } }); + + expect(HttpLogic.actions.setErrorConnecting).not.toHaveBeenCalled(); + }); + + it('does not handle errors for unrelated calls', async () => { + const { responseError } = mockHttp.intercept.mock.calls[0][0] as any; + await responseError({ response: { url: '/api/some_other_plugin/', status: 502 } }); + + expect(HttpLogic.actions.setErrorConnecting).not.toHaveBeenCalled(); + }); + }); + }); + + it('sets httpInterceptors and calls all valid remove functions on unmount', () => { + const unmount = HttpLogic.mount(); + const httpInterceptors = [jest.fn(), undefined, jest.fn()] as any; + + HttpLogic.actions.setHttpInterceptors(httpInterceptors); + expect(HttpLogic.values.httpInterceptors).toEqual(httpInterceptors); + + unmount(); + expect(httpInterceptors[0]).toHaveBeenCalledTimes(1); + expect(httpInterceptors[2]).toHaveBeenCalledTimes(1); + }); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts new file mode 100644 index 0000000000000..7bf7a19ed451f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_logic.ts @@ -0,0 +1,86 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import { kea } from 'kea'; + +import { HttpSetup } from 'src/core/public'; + +import { IKeaLogic, IKeaParams, TKeaReducers } from '../../shared/types'; + +export interface IHttpLogicValues { + http: HttpSetup; + httpInterceptors: Function[]; + errorConnecting: boolean; +} +export interface IHttpLogicActions { + initializeHttp({ http, errorConnecting }: { http: HttpSetup; errorConnecting?: boolean }): void; + initializeHttpInterceptors(): void; + setHttpInterceptors(httpInterceptors: Function[]): void; + setErrorConnecting(errorConnecting: boolean): void; +} + +export const HttpLogic = kea({ + actions: (): IHttpLogicActions => ({ + initializeHttp: ({ http, errorConnecting }) => ({ http, errorConnecting }), + initializeHttpInterceptors: () => null, + setHttpInterceptors: (httpInterceptors) => ({ httpInterceptors }), + setErrorConnecting: (errorConnecting) => ({ errorConnecting }), + }), + reducers: (): TKeaReducers => ({ + http: [ + (null as unknown) as HttpSetup, + { + initializeHttp: (_, { http }) => http, + }, + ], + httpInterceptors: [ + [], + { + setHttpInterceptors: (_, { httpInterceptors }) => httpInterceptors, + }, + ], + errorConnecting: [ + false, + { + initializeHttp: (_, { errorConnecting }) => !!errorConnecting, + setErrorConnecting: (_, { errorConnecting }) => errorConnecting, + }, + ], + }), + listeners: ({ values, actions }) => ({ + initializeHttpInterceptors: () => { + const httpInterceptors = []; + + const errorConnectingInterceptor = values.http.intercept({ + responseError: async (httpResponse) => { + const { url, status } = httpResponse.response!; + const hasErrorConnecting = status === 502; + const isApiResponse = + url.includes('/api/app_search/') || url.includes('/api/workplace_search/'); + + if (isApiResponse && hasErrorConnecting) { + actions.setErrorConnecting(true); + } + return httpResponse; + }, + }); + httpInterceptors.push(errorConnectingInterceptor); + + // TODO: Read only mode interceptor + actions.setHttpInterceptors(httpInterceptors); + }, + }), + events: ({ values }) => ({ + beforeUnmount: () => { + values.httpInterceptors.forEach((removeInterceptorFn?: Function) => { + if (removeInterceptorFn) removeInterceptorFn(); + }); + }, + }), +} as IKeaParams) as IKeaLogic< + IHttpLogicValues, + IHttpLogicActions +>; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx new file mode 100644 index 0000000000000..81106235780d6 --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.test.tsx @@ -0,0 +1,44 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import '../../__mocks__/shallow_usecontext.mock'; +import '../../__mocks__/kea.mock'; + +import React from 'react'; +import { shallow } from 'enzyme'; +import { useActions } from 'kea'; + +import { HttpProvider } from './'; + +describe('HttpProvider', () => { + const props = { + http: {} as any, + errorConnecting: false, + }; + const initializeHttp = jest.fn(); + const initializeHttpInterceptors = jest.fn(); + + beforeEach(() => { + jest.clearAllMocks(); + (useActions as jest.Mock).mockImplementationOnce(() => ({ + initializeHttp, + initializeHttpInterceptors, + })); + }); + + it('does not render', () => { + const wrapper = shallow(); + + expect(wrapper.isEmptyRender()).toBe(true); + }); + + it('calls initialization actions on mount', () => { + shallow(); + + expect(initializeHttp).toHaveBeenCalledWith(props); + expect(initializeHttpInterceptors).toHaveBeenCalled(); + }); +}); diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx new file mode 100644 index 0000000000000..6febc1869054f --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/http_provider.tsx @@ -0,0 +1,28 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +import React, { useEffect } from 'react'; +import { useActions } from 'kea'; + +import { HttpSetup } from 'src/core/public'; + +import { HttpLogic, IHttpLogicActions } from './http_logic'; + +interface IHttpProviderProps { + http: HttpSetup; + errorConnecting?: boolean; +} + +export const HttpProvider: React.FC = (props) => { + const { initializeHttp, initializeHttpInterceptors } = useActions(HttpLogic) as IHttpLogicActions; + + useEffect(() => { + initializeHttp(props); + initializeHttpInterceptors(); + }, []); + + return null; +}; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts b/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts new file mode 100644 index 0000000000000..449ff9d56debf --- /dev/null +++ b/x-pack/plugins/enterprise_search/public/applications/shared/http/index.ts @@ -0,0 +1,8 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + +export { HttpLogic, IHttpLogicValues, IHttpLogicActions } from './http_logic'; +export { HttpProvider } from './http_provider'; diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts index 74bb53ef3a954..a8e08323c5e3b 100644 --- a/x-pack/plugins/enterprise_search/public/applications/shared/types.ts +++ b/x-pack/plugins/enterprise_search/public/applications/shared/types.ts @@ -14,7 +14,7 @@ export interface IFlashMessagesProps { } export interface IKeaLogic { - mount(): void; + mount(): Function; values: IKeaValues; actions: IKeaActions; } @@ -33,6 +33,7 @@ export interface IKeaLogic { export interface IKeaParams { selectors?(params: { selectors: IKeaValues }): void; listeners?(params: { actions: IKeaActions; values: IKeaValues }): void; + events?(params: { actions: IKeaActions; values: IKeaValues }): void; } /** @@ -47,7 +48,10 @@ export type TKeaReducers = { [Value in keyof IKeaValues]?: [ IKeaValues[Value], { - [Action in keyof IKeaActions]?: (state: IKeaValues, payload: IKeaValues) => IKeaValues[Value]; + [Action in keyof IKeaActions]?: ( + state: IKeaValues[Value], + payload: IKeaValues + ) => IKeaValues[Value]; } ]; }; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts index 395d2044e7dbc..5588c4fc53b67 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/__mocks__/overview_logic.mock.ts @@ -22,7 +22,6 @@ export const mockLogicValues = { personalSourcesCount: 0, sourcesCount: 0, dataLoading: true, - hasErrorConnecting: false, flashMessages: {}, } as IOverviewValues; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx index 744fd8aeb1951..fee966a56923d 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.test.tsx @@ -11,7 +11,6 @@ import { mockLogicActions, setMockValues } from './__mocks__'; import React from 'react'; import { shallow, mount } from 'enzyme'; -import { ErrorState } from '../error_state'; import { Loading } from '../shared/loading'; import { ViewContentHeader } from '../shared/view_content_header'; @@ -27,13 +26,6 @@ describe('Overview', () => { expect(wrapper.find(Loading)).toHaveLength(1); }); - - it('hasErrorConnecting', () => { - setMockValues({ hasErrorConnecting: true }); - const wrapper = shallow(); - - expect(wrapper.find(ErrorState)).toHaveLength(1); - }); }); describe('happy-path states', () => { diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx index b816eb2973207..6aa3e1e608bfe 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview.tsx @@ -6,19 +6,16 @@ // TODO: Remove EuiPage & EuiPageBody before exposing full app -import React, { useContext, useEffect } from 'react'; +import React, { useEffect } from 'react'; import { EuiPage, EuiPageBody, EuiSpacer } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { useActions, useValues } from 'kea'; import { SetWorkplaceSearchChrome as SetPageChrome } from '../../../shared/kibana_chrome'; import { SendWorkplaceSearchTelemetry as SendTelemetry } from '../../../shared/telemetry'; -import { KibanaContext, IKibanaContext } from '../../../index'; import { OverviewLogic, IOverviewActions, IOverviewValues } from './overview_logic'; -import { ErrorState } from '../error_state'; - import { Loading } from '../shared/loading'; import { ProductButton } from '../shared/product_button'; import { ViewContentHeader } from '../shared/view_content_header'; @@ -47,13 +44,10 @@ const HEADER_DESCRIPTION = i18n.translate( ); export const Overview: React.FC = () => { - const { http } = useContext(KibanaContext) as IKibanaContext; - const { initializeOverview } = useActions(OverviewLogic) as IOverviewActions; const { dataLoading, - hasErrorConnecting, hasUsers, hasOrgSources, isOldAccount, @@ -61,10 +55,9 @@ export const Overview: React.FC = () => { } = useValues(OverviewLogic) as IOverviewValues; useEffect(() => { - initializeOverview({ http }); + initializeOverview(); }, [initializeOverview]); - if (hasErrorConnecting) return ; if (dataLoading) return ; const hideOnboarding = hasUsers && hasOrgSources && isOldAccount && orgName !== defaultOrgName; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts index 7df4de4719f31..3fbf0e60b5b49 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.test.ts @@ -5,24 +5,18 @@ */ import { resetContext } from 'kea'; -import { act } from 'react-dom/test-utils'; -import { mockKibanaContext } from '../../../__mocks__'; +jest.mock('../../../shared/http', () => ({ HttpLogic: { values: { http: { get: jest.fn() } } } })); +import { HttpLogic } from '../../../shared/http'; import { mockLogicValues } from './__mocks__'; import { OverviewLogic } from './overview_logic'; describe('OverviewLogic', () => { - let unmount: any; - beforeEach(() => { - resetContext({}); - unmount = OverviewLogic.mount() as any; jest.clearAllMocks(); - }); - - afterEach(() => { - unmount(); + resetContext({}); + OverviewLogic.mount(); }); it('has expected default values', () => { @@ -91,48 +85,14 @@ describe('OverviewLogic', () => { }); }); - describe('setHasErrorConnecting', () => { - it('will set `hasErrorConnecting`', () => { - OverviewLogic.actions.setHasErrorConnecting(true); - - expect(OverviewLogic.values.hasErrorConnecting).toEqual(true); - expect(OverviewLogic.values.dataLoading).toEqual(false); - }); - }); - describe('initializeOverview', () => { it('calls API and sets values', async () => { - const mockHttp = mockKibanaContext.http; - const mockApi = jest.fn(() => mockLogicValues as any); const setServerDataSpy = jest.spyOn(OverviewLogic.actions, 'setServerData'); - await act(async () => - OverviewLogic.actions.initializeOverview({ - http: { - ...mockHttp, - get: mockApi, - }, - }) - ); + await OverviewLogic.actions.initializeOverview(); - expect(mockApi).toHaveBeenCalledWith('/api/workplace_search/overview'); + expect(HttpLogic.values.http.get).toHaveBeenCalledWith('/api/workplace_search/overview'); expect(setServerDataSpy).toHaveBeenCalled(); }); - - it('handles error state', async () => { - const mockHttp = mockKibanaContext.http; - const setHasErrorConnectingSpy = jest.spyOn(OverviewLogic.actions, 'setHasErrorConnecting'); - - await act(async () => - OverviewLogic.actions.initializeOverview({ - http: { - ...mockHttp, - get: () => Promise.reject(), - }, - }) - ); - - expect(setHasErrorConnectingSpy).toHaveBeenCalled(); - }); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts index 8bb177a2e742b..057bce1b4056c 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/components/overview/overview_logic.ts @@ -4,9 +4,8 @@ * you may not use this file except in compliance with the Elastic License. */ -import { HttpSetup } from 'src/core/public'; - import { kea } from 'kea'; +import { HttpLogic } from '../../../shared/http'; import { IAccount, IOrganization } from '../../types'; import { IFlashMessagesProps, IKeaLogic, TKeaReducers, IKeaParams } from '../../../shared/types'; @@ -32,13 +31,11 @@ export interface IOverviewServerData { export interface IOverviewActions { setServerData(serverData: IOverviewServerData): void; setFlashMessages(flashMessages: IFlashMessagesProps): void; - setHasErrorConnecting(hasErrorConnecting: boolean): void; - initializeOverview({ http }: { http: HttpSetup }): void; + initializeOverview(): void; } export interface IOverviewValues extends IOverviewServerData { dataLoading: boolean; - hasErrorConnecting: boolean; flashMessages: IFlashMessagesProps; } @@ -46,8 +43,7 @@ export const OverviewLogic = kea({ actions: (): IOverviewActions => ({ setServerData: (serverData) => serverData, setFlashMessages: (flashMessages) => ({ flashMessages }), - setHasErrorConnecting: (hasErrorConnecting) => ({ hasErrorConnecting }), - initializeOverview: ({ http }) => ({ http }), + initializeOverview: () => null, }), reducers: (): TKeaReducers => ({ organization: [ @@ -138,24 +134,13 @@ export const OverviewLogic = kea({ true, { setServerData: () => false, - setHasErrorConnecting: () => false, - }, - ], - hasErrorConnecting: [ - false, - { - setHasErrorConnecting: (_, { hasErrorConnecting }) => hasErrorConnecting, }, ], }), listeners: ({ actions }): Partial => ({ - initializeOverview: async ({ http }: { http: HttpSetup }) => { - try { - const response = await http.get('/api/workplace_search/overview'); - actions.setServerData(response); - } catch (error) { - actions.setHasErrorConnecting(true); - } + initializeOverview: async () => { + const response = await HttpLogic.values.http.get('/api/workplace_search/overview'); + actions.setServerData(response); }, }), } as IKeaParams) as IKeaLogic; diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx index a55ff64014130..654f4dce0ebf3 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.test.tsx @@ -5,35 +5,44 @@ */ import '../__mocks__/shallow_usecontext.mock'; +import '../__mocks__/kea.mock'; import React, { useContext } from 'react'; import { Redirect } from 'react-router-dom'; import { shallow } from 'enzyme'; +import { useValues } from 'kea'; import { Overview } from './components/overview'; +import { ErrorState } from './components/error_state'; import { WorkplaceSearch } from './'; describe('Workplace Search', () => { - describe('/', () => { - it('redirects to Setup Guide when enterpriseSearchUrl is not set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ - config: { host: '' }, - })); - const wrapper = shallow(); - - expect(wrapper.find(Redirect)).toHaveLength(1); - expect(wrapper.find(Overview)).toHaveLength(0); - }); - - it('renders the Overview when enterpriseSearchUrl is set', () => { - (useContext as jest.Mock).mockImplementationOnce(() => ({ - config: { host: 'https://foo.bar' }, - })); - const wrapper = shallow(); - - expect(wrapper.find(Overview)).toHaveLength(1); - expect(wrapper.find(Redirect)).toHaveLength(0); - }); + it('redirects to Setup Guide when enterpriseSearchUrl is not set', () => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ + config: { host: '' }, + })); + const wrapper = shallow(); + + expect(wrapper.find(Redirect)).toHaveLength(1); + expect(wrapper.find(Overview)).toHaveLength(0); + }); + + it('renders the Overview when enterpriseSearchUrl is set', () => { + (useContext as jest.Mock).mockImplementationOnce(() => ({ + config: { host: 'https://foo.bar' }, + })); + const wrapper = shallow(); + + expect(wrapper.find(Overview)).toHaveLength(1); + expect(wrapper.find(Redirect)).toHaveLength(0); + }); + + it('renders ErrorState when the app cannot connect to Enterprise Search', () => { + (useValues as jest.Mock).mockImplementationOnce(() => ({ errorConnecting: true })); + const wrapper = shallow(); + + expect(wrapper.find(ErrorState).exists()).toBe(true); + expect(wrapper.find(Overview)).toHaveLength(0); }); }); diff --git a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx index 94462aa8de7d1..b261c83e30dde 100644 --- a/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx +++ b/x-pack/plugins/enterprise_search/public/applications/workplace_search/index.tsx @@ -6,19 +6,24 @@ import React, { useContext } from 'react'; import { Route, Redirect, Switch } from 'react-router-dom'; +import { useValues } from 'kea'; import { IInitialAppData } from '../../../common/types'; import { KibanaContext, IKibanaContext } from '../index'; +import { HttpLogic, IHttpLogicValues } from '../shared/http'; import { Layout } from '../shared/layout'; import { WorkplaceSearchNav } from './components/layout/nav'; import { SETUP_GUIDE_PATH } from './routes'; import { SetupGuide } from './components/setup_guide'; +import { ErrorState } from './components/error_state'; import { Overview } from './components/overview'; export const WorkplaceSearch: React.FC = (props) => { const { config } = useContext(KibanaContext) as IKibanaContext; + const { errorConnecting } = useValues(HttpLogic) as IHttpLogicValues; + if (!config.host) return ( @@ -37,16 +42,20 @@ export const WorkplaceSearch: React.FC = (props) => { - + {errorConnecting ? : } }> - - - {/* Will replace with groups component subsequent PR */} -
- - + {errorConnecting ? ( + + ) : ( + + + {/* Will replace with groups component subsequent PR */} +
+ + + )} diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts index 148a50fb4a5ce..881fe02af5b09 100644 --- a/x-pack/plugins/enterprise_search/public/plugin.ts +++ b/x-pack/plugins/enterprise_search/public/plugin.ts @@ -31,6 +31,7 @@ export interface ClientConfigType { } export interface ClientData extends IInitialAppData { externalUrl: IExternalUrl; + errorConnecting?: boolean; } export interface PluginsSetup { @@ -123,6 +124,7 @@ export class EnterpriseSearchPlugin implements Plugin { this.hasInitialized = true; } catch { + this.data.errorConnecting = true; // The plugin will attempt to re-fetch config data on page change } } diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts index 968ecb95fd931..1ea023ecacdbe 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.test.ts @@ -68,10 +68,11 @@ describe('engine routes', () => { ).andReturnError(); }); - it('should return 404 with a message', async () => { + it('should return 502 with a message', async () => { await mockRouter.callRoute(mockRequest); - expect(mockRouter.response.notFound).toHaveBeenCalledWith({ + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + statusCode: 502, body: 'cannot-connect', }); expect(mockLogger.error).toHaveBeenCalledWith('Cannot connect to App Search: Failed'); @@ -87,10 +88,11 @@ describe('engine routes', () => { ).andReturnInvalidData(); }); - it('should return 404 with a message', async () => { + it('should return 502 with a message', async () => { await mockRouter.callRoute(mockRequest); - expect(mockRouter.response.notFound).toHaveBeenCalledWith({ + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + statusCode: 502, body: 'cannot-connect', }); expect(mockLogger.error).toHaveBeenCalledWith( diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts index ca83c0e187ddb..7190772fb92bb 100644 --- a/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts +++ b/x-pack/plugins/enterprise_search/server/routes/app_search/engines.ts @@ -52,7 +52,7 @@ export function registerEnginesRoute({ router, config, log }: IRouteDependencies log.error(`Cannot connect to App Search: ${e.toString()}`); if (e instanceof Error) log.debug(e.stack as string); - return response.notFound({ body: 'cannot-connect' }); + return response.customError({ statusCode: 502, body: 'cannot-connect' }); } } ); diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.test.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.test.ts index 3a4e28b0de5ff..f6534b27b5da0 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.test.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.test.ts @@ -63,10 +63,11 @@ describe('engine routes', () => { }).andReturnError(); }); - it('should return 404 with a message', async () => { + it('should return 502 with a message', async () => { await mockRouter.callRoute(mockRequest); - expect(mockRouter.response.notFound).toHaveBeenCalledWith({ + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + statusCode: 502, body: 'cannot-connect', }); expect(mockLogger.error).toHaveBeenCalledWith('Cannot connect to Workplace Search: Failed'); @@ -81,10 +82,11 @@ describe('engine routes', () => { }).andReturnInvalidData(); }); - it('should return 404 with a message', async () => { + it('should return 502 with a message', async () => { await mockRouter.callRoute(mockRequest); - expect(mockRouter.response.notFound).toHaveBeenCalledWith({ + expect(mockRouter.response.customError).toHaveBeenCalledWith({ + statusCode: 502, body: 'cannot-connect', }); expect(mockLogger.error).toHaveBeenCalledWith( diff --git a/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.ts b/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.ts index d1e2f4f5f180d..9e5d94ac1b4fe 100644 --- a/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.ts +++ b/x-pack/plugins/enterprise_search/server/routes/workplace_search/overview.ts @@ -39,7 +39,7 @@ export function registerWSOverviewRoute({ router, config, log }: IRouteDependenc log.error(`Cannot connect to Workplace Search: ${e.toString()}`); if (e instanceof Error) log.debug(e.stack as string); - return response.notFound({ body: 'cannot-connect' }); + return response.customError({ statusCode: 502, body: 'cannot-connect' }); } } );