diff --git a/.eslintrc.js b/.eslintrc.js
index 8d5b4525d51ba..4d97d54e2da04 100644
--- a/.eslintrc.js
+++ b/.eslintrc.js
@@ -905,6 +905,18 @@ module.exports = {
},
},
+ /**
+ * Enterprise Search overrides
+ */
+ {
+ files: ['x-pack/plugins/enterprise_search/**/*.{ts,tsx}'],
+ excludedFiles: ['x-pack/plugins/enterprise_search/**/*.{test,mock}.{ts,tsx}'],
+ rules: {
+ 'react-hooks/exhaustive-deps': 'off',
+ '@typescript-eslint/no-explicit-any': 'error',
+ },
+ },
+
/**
* disable jsx-a11y for kbn-ui-framework
*/
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx
index 7d0716ce0cdd0..dfcda544459d4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/mount_with_context.mock.tsx
@@ -22,7 +22,7 @@ import { mockLicenseContext } from './license_context.mock';
*
* const wrapper = mountWithContext(, { enterpriseSearchUrl: 'someOverride', license: {} });
*/
-export const mountWithContext = (children, context) => {
+export const mountWithContext = (children: React.ReactNode, context?: object) => {
return mount(
@@ -40,7 +40,7 @@ export const mountWithContext = (children, context) => {
*
* Same usage/override functionality as mountWithContext
*/
-export const mountWithKibanaContext = (children, context) => {
+export const mountWithKibanaContext = (children: React.ReactNode, context?: object) => {
return mount(
{children}
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
index 20add45e16b58..767a52a75d1fb 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_usecontext.mock.ts
@@ -12,7 +12,7 @@ import { mockKibanaContext } from './kibana_context.mock';
import { mockLicenseContext } from './license_context.mock';
jest.mock('react', () => ({
- ...jest.requireActual('react'),
+ ...(jest.requireActual('react') as object),
useContext: jest.fn(() => ({ ...mockKibanaContext, ...mockLicenseContext })),
}));
diff --git a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_with_i18n.mock.tsx b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_with_i18n.mock.tsx
index 7815bb71fa50e..ae7d0b09f9872 100644
--- a/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_with_i18n.mock.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/__mocks__/shallow_with_i18n.mock.tsx
@@ -20,12 +20,11 @@ const { intl } = intlProvider.getChildContext();
*
* const wrapper = shallowWithIntl();
*/
-export const shallowWithIntl = (children) => {
- return shallow({children}, {
- context: { intl },
- childContextTypes: { intl },
- })
+export const shallowWithIntl = (children: React.ReactNode) => {
+ const context = { context: { intl } };
+
+ return shallow({children}, context)
.childAt(0)
- .dive()
+ .dive(context)
.shallow();
};
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/empty_states/empty_state.tsx
index 91b1a4319cbd7..26ed01cc614dc 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/empty_states/empty_state.tsx
@@ -16,7 +16,7 @@ import { EngineOverviewHeader } from '../engine_overview_header';
import './empty_states.scss';
-export const EmptyState: React.FC<> = () => {
+export const EmptyState: React.FC = () => {
const { enterpriseSearchUrl, http } = useContext(KibanaContext) as IKibanaContext;
const buttonProps = {
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/empty_states/empty_states.test.tsx
index bb37229998ed2..2d2f92c2f7b1f 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/empty_states/empty_states.test.tsx
@@ -39,7 +39,8 @@ describe('NoUserState', () => {
});
it('renders with username', () => {
- getUserName.mockImplementationOnce(() => 'dolores-abernathy');
+ (getUserName as jest.Mock).mockImplementationOnce(() => 'dolores-abernathy');
+
const wrapper = shallowWithIntl();
const prompt = wrapper.find(EuiEmptyPrompt).dive();
const description1 = prompt.find(FormattedMessage).at(1).dive();
@@ -62,7 +63,7 @@ describe('EmptyState', () => {
button.simulate('click');
expect(sendTelemetry).toHaveBeenCalled();
- sendTelemetry.mockClear();
+ (sendTelemetry as jest.Mock).mockClear();
});
});
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/empty_states/error_state.tsx
index 039e645a27126..5891c89c3a022 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/empty_states/error_state.tsx
@@ -16,7 +16,7 @@ import { EngineOverviewHeader } from '../engine_overview_header';
import './empty_states.scss';
-export const ErrorState: ReactFC<> = () => {
+export const ErrorState: React.FC = () => {
const { enterpriseSearchUrl } = useContext(KibanaContext) as IKibanaContext;
return (
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/empty_states/loading_state.tsx
index 5c1d0c744f743..3d69fe6126273 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/empty_states/loading_state.tsx
@@ -12,7 +12,7 @@ import { EngineOverviewHeader } from '../engine_overview_header';
import './empty_states.scss';
-export const LoadingState: React.FC<> = () => {
+export const LoadingState: React.FC = () => {
return (
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx
index c1d6c2bcffe41..bf728bd43ead0 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/empty_states/no_user_state.tsx
@@ -15,7 +15,7 @@ import { getUserName } from '../../utils/get_username';
import './empty_states.scss';
-export const NoUserState: React.FC<> = () => {
+export const NoUserState: React.FC = () => {
const username = getUserName();
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 a9670163e76b8..18cf3dade2056 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
@@ -8,7 +8,7 @@ import '../../../__mocks__/react_router_history.mock';
import React from 'react';
import { act } from 'react-dom/test-utils';
-import { render } from 'enzyme';
+import { render, ReactWrapper } from 'enzyme';
import { I18nProvider } from '@kbn/i18n/react';
import { KibanaContext } from '../../../';
@@ -16,7 +16,7 @@ import { LicenseContext } from '../../../shared/licensing';
import { mountWithContext, mockKibanaContext } from '../../../__mocks__';
import { EmptyState, ErrorState, NoUserState } from '../empty_states';
-import { EngineTable } from './engine_table';
+import { EngineTable, IEngineTablePagination } from './engine_table';
import { EngineOverview } from './';
@@ -25,7 +25,7 @@ describe('EngineOverview', () => {
it('isLoading', () => {
// We use render() instead of mount() here to not trigger lifecycle methods (i.e., useEffect)
// TODO: Consider pulling this out to a renderWithContext mock/helper
- const wrapper = render(
+ const wrapper: Cheerio = render(
@@ -85,7 +85,7 @@ describe('EngineOverview', () => {
},
};
const mockApi = jest.fn(() => mockedApiResponse);
- let wrapper;
+ let wrapper: ReactWrapper;
beforeAll(async () => {
wrapper = await mountWithApiMock({ get: mockApi });
@@ -105,7 +105,8 @@ describe('EngineOverview', () => {
});
describe('pagination', () => {
- const getTablePagination = () => wrapper.find(EngineTable).first().prop('pagination');
+ const getTablePagination: () => IEngineTablePagination = () =>
+ wrapper.find(EngineTable).first().prop('pagination');
it('passes down page data from the API', () => {
const pagination = getTablePagination();
@@ -156,8 +157,8 @@ describe('EngineOverview', () => {
* Test helpers
*/
- const mountWithApiMock = async ({ get, license }) => {
- let wrapper;
+ const mountWithApiMock = async ({ get, license }: { get(): any; license?: object }) => {
+ let wrapper: ReactWrapper | undefined;
const httpMock = { ...mockKibanaContext.http, get };
// We get a lot of act() warning/errors in the terminal without this.
@@ -166,8 +167,12 @@ describe('EngineOverview', () => {
await act(async () => {
wrapper = mountWithContext(, { http: httpMock, license });
});
- wrapper.update(); // This seems to be required for the DOM to actually update
+ if (wrapper) {
+ wrapper.update(); // This seems to be required for the DOM to actually update
- return wrapper;
+ return wrapper;
+ } else {
+ throw new Error('Could not mount wrapper');
+ }
};
});
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 a1e4a11dc2daf..479dfe8e61513 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
@@ -30,7 +30,7 @@ import { EngineTable } from './engine_table';
import './engine_overview.scss';
-export const EngineOverview: ReactFC<> = () => {
+export const EngineOverview: React.FC = () => {
const { http } = useContext(KibanaContext) as IKibanaContext;
const { license } = useContext(LicenseContext) as ILicenseContext;
@@ -45,12 +45,12 @@ export const EngineOverview: ReactFC<> = () => {
const [metaEnginesPage, setMetaEnginesPage] = useState(1);
const [metaEnginesTotal, setMetaEnginesTotal] = useState(0);
- const getEnginesData = async ({ type, pageIndex }) => {
+ const getEnginesData = async ({ type, pageIndex }: IGetEnginesParams) => {
return await http.get('/api/app_search/engines', {
query: { type, pageIndex },
});
};
- const setEnginesData = async (params, callbacks) => {
+ const setEnginesData = async (params: IGetEnginesParams, callbacks: ISetEnginesCallbacks) => {
try {
const response = await getEnginesData(params);
@@ -72,7 +72,7 @@ export const EngineOverview: ReactFC<> = () => {
const callbacks = { setResults: setEngines, setResultsTotal: setEnginesTotal };
setEnginesData(params, callbacks);
- }, [enginesPage]); // eslint-disable-line react-hooks/exhaustive-deps
+ }, [enginesPage]);
useEffect(() => {
if (hasPlatinumLicense(license)) {
@@ -81,7 +81,7 @@ export const EngineOverview: ReactFC<> = () => {
setEnginesData(params, callbacks);
}
- }, [license, metaEnginesPage]); // eslint-disable-line react-hooks/exhaustive-deps
+ }, [license, metaEnginesPage]);
if (hasErrorConnecting) return ;
if (hasNoAccount) return ;
@@ -150,3 +150,16 @@ export const EngineOverview: ReactFC<> = () => {
);
};
+
+/**
+ * Type definitions
+ */
+
+interface IGetEnginesParams {
+ type: string;
+ pageIndex: number;
+}
+interface ISetEnginesCallbacks {
+ setResults: React.Dispatch>;
+ setResultsTotal: React.Dispatch>;
+}
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx
index 1665726251bd6..46b6e61e352de 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.test.tsx
@@ -72,9 +72,9 @@ describe('EngineTable', () => {
it('handles empty data', () => {
const emptyWrapper = mountWithContext(
-
+ {} }} />
);
- const emptyTable = wrapper.find(EuiBasicTable);
+ const emptyTable = emptyWrapper.find(EuiBasicTable);
expect(emptyTable.prop('pagination').pageIndex).toEqual(0);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx
index d565856f9675d..1e58d820dc83b 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/engine_overview/engine_table.tsx
@@ -5,7 +5,7 @@
*/
import React, { useContext } from 'react';
-import { EuiBasicTable, EuiLink } from '@elastic/eui';
+import { EuiBasicTable, EuiBasicTableColumn, EuiLink } from '@elastic/eui';
import { FormattedMessage, FormattedDate, FormattedNumber } from '@kbn/i18n/react';
import { i18n } from '@kbn/i18n';
@@ -14,31 +14,33 @@ import { KibanaContext, IKibanaContext } from '../../../index';
import { ENGINES_PAGE_SIZE } from '../../../../../common/constants';
-interface IEngineTableProps {
- data: Array<{
- name: string;
- created_at: string;
- document_count: number;
- field_count: number;
- }>;
- pagination: {
- totalEngines: number;
- pageIndex: number;
- onPaginate(pageIndex: number);
- };
+export interface IEngineTableData {
+ name: string;
+ created_at: string;
+ document_count: number;
+ field_count: number;
+}
+export interface IEngineTablePagination {
+ totalEngines: number;
+ pageIndex: number;
+ onPaginate(pageIndex: number): void;
+}
+export interface IEngineTableProps {
+ data: IEngineTableData[];
+ pagination: IEngineTablePagination;
}
-interface IOnChange {
+export interface IOnChange {
page: {
index: number;
};
}
-export const EngineTable: ReactFC = ({
+export const EngineTable: React.FC = ({
data,
- pagination: { totalEngines, pageIndex = 0, onPaginate },
+ pagination: { totalEngines, pageIndex, onPaginate },
}) => {
const { enterpriseSearchUrl, http } = useContext(KibanaContext) as IKibanaContext;
- const engineLinkProps = (name) => ({
+ const engineLinkProps = (name: string) => ({
href: `${enterpriseSearchUrl}/as/engines/${name}`,
target: '_blank',
onClick: () =>
@@ -50,13 +52,13 @@ export const EngineTable: ReactFC = ({
}),
});
- const columns = [
+ const columns: Array> = [
{
field: 'name',
name: i18n.translate('xpack.enterpriseSearch.appSearch.enginesOverview.table.column.name', {
defaultMessage: 'Name',
}),
- render: (name) => (
+ render: (name: string) => (
{name}
@@ -65,6 +67,8 @@ export const EngineTable: ReactFC = ({
truncateText: true,
mobileOptions: {
header: true,
+ // Note: the below props are valid props per https://elastic.github.io/eui/#/tabular-content/tables (Responsive tables), but EUI's types have a bug reporting it as an error
+ // @ts-ignore
enlarge: true,
fullWidth: true,
truncateText: false,
@@ -79,7 +83,7 @@ export const EngineTable: ReactFC = ({
}
),
dataType: 'string',
- render: (dateString) => (
+ render: (dateString: string) => (
// e.g., January 1, 1970
),
@@ -93,7 +97,7 @@ export const EngineTable: ReactFC = ({
}
),
dataType: 'number',
- render: (number) => ,
+ render: (number: number) => ,
truncateText: true,
},
{
@@ -105,7 +109,7 @@ export const EngineTable: ReactFC = ({
}
),
dataType: 'number',
- render: (number) => ,
+ render: (number: number) => ,
truncateText: true,
},
{
@@ -117,7 +121,7 @@ export const EngineTable: ReactFC = ({
}
),
dataType: 'string',
- render: (name) => (
+ render: (name: string) => (
= ({
totalItemCount: totalEngines,
hidePerPageOptions: true,
}}
- onChange={({ page }): IOnChange => {
+ onChange={({ page }: IOnChange) => {
const { index } = page;
onPaginate(index + 1); // Note on paging - App Search's API pages start at 1, EuiBasicTables' pages start at 0
}}
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_header/engine_overview_header.test.tsx
index 9663eb4ef61af..2e49540270ef0 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_header/engine_overview_header.test.tsx
@@ -24,8 +24,8 @@ describe('EngineOverviewHeader', () => {
const wrapper = shallow();
const button = wrapper.find('[data-test-subj="launchButton"]');
- expect(button.props().href).toBe('http://localhost:3002/as');
- expect(button.props().isDisabled).toBeFalsy();
+ expect(button.prop('href')).toBe('http://localhost:3002/as');
+ expect(button.prop('isDisabled')).toBeFalsy();
button.simulate('click');
expect(sendTelemetry).toHaveBeenCalled();
@@ -35,7 +35,7 @@ describe('EngineOverviewHeader', () => {
const wrapper = shallow();
const button = wrapper.find('[data-test-subj="launchButton"]');
- expect(button.props().isDisabled).toBe(true);
- expect(button.props().href).toBeUndefined();
+ expect(button.prop('isDisabled')).toBe(true);
+ expect(button.prop('href')).toBeUndefined();
});
});
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_header/engine_overview_header.tsx
index 650a864f5e615..9aafa8ec0380c 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_header/engine_overview_header.tsx
@@ -5,7 +5,14 @@
*/
import React, { useContext } from 'react';
-import { EuiPageHeader, EuiPageHeaderSection, EuiTitle, EuiButton } from '@elastic/eui';
+import {
+ EuiPageHeader,
+ EuiPageHeaderSection,
+ EuiTitle,
+ EuiButton,
+ EuiButtonProps,
+ EuiLinkProps,
+} from '@elastic/eui';
import { FormattedMessage } from '@kbn/i18n/react';
import { sendTelemetry } from '../../../shared/telemetry';
@@ -24,7 +31,8 @@ export const EngineOverviewHeader: React.FC = ({
fill: true,
iconType: 'popout',
'data-test-subj': 'launchButton',
- };
+ } as EuiButtonProps & EuiLinkProps;
+
if (isButtonDisabled) {
buttonProps.isDisabled = true;
} else {
diff --git a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
index 855449b0c0bcd..3e290a7777f1c 100644
--- a/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/app_search/components/setup_guide/setup_guide.tsx
@@ -15,7 +15,6 @@ import {
EuiFlexItem,
EuiTitle,
EuiText,
- EuiImage,
EuiIcon,
EuiSteps,
EuiCode,
@@ -32,7 +31,7 @@ import { SendAppSearchTelemetry as SendTelemetry } from '../../../shared/telemet
import GettingStarted from '../../assets/getting_started.png';
import './setup_guide.scss';
-export const SetupGuide: React.FC<> = () => {
+export const SetupGuide: React.FC = () => {
return (
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 d11c47475089d..45e318ca0f9d9 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
@@ -18,7 +18,7 @@ import { AppSearch } from './';
describe('App Search Routes', () => {
describe('/', () => {
it('redirects to Setup Guide when enterpriseSearchUrl is not set', () => {
- useContext.mockImplementationOnce(() => ({ enterpriseSearchUrl: '' }));
+ (useContext as jest.Mock).mockImplementationOnce(() => ({ enterpriseSearchUrl: '' }));
const wrapper = shallow();
expect(wrapper.find(Redirect)).toHaveLength(1);
@@ -26,7 +26,9 @@ describe('App Search Routes', () => {
});
it('renders Engine Overview when enterpriseSearchUrl is set', () => {
- useContext.mockImplementationOnce(() => ({ enterpriseSearchUrl: 'https://foo.bar' }));
+ (useContext as jest.Mock).mockImplementationOnce(() => ({
+ enterpriseSearchUrl: 'https://foo.bar',
+ }));
const wrapper = shallow();
expect(wrapper.find(EngineOverview)).toHaveLength(1);
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 9afc3c9fd9761..8f7142f1631a9 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
@@ -12,7 +12,7 @@ import { KibanaContext, IKibanaContext } from '../index';
import { SetupGuide } from './components/setup_guide';
import { EngineOverview } from './components/engine_overview';
-export const AppSearch: React.FC<> = () => {
+export const AppSearch: React.FC = () => {
const { enterpriseSearchUrl } = useContext(KibanaContext) as IKibanaContext;
return (
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
index fd88fc32ff4ae..ef69ba7e40cf3 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/index.test.tsx
@@ -18,14 +18,14 @@ describe('renderApp', () => {
const config = {};
const plugins = {
licensing: licensingMock.createSetup(),
- };
+ } as any;
beforeEach(() => {
jest.clearAllMocks();
});
it('mounts and unmounts UI', () => {
- const MockApp: React.FC = () => Hello world!
;
+ const MockApp = () => Hello world!
;
const unmount = renderApp(MockApp, core, params, config, plugins);
expect(params.element.querySelector('.hello-world')).not.toBeNull();
diff --git a/x-pack/plugins/enterprise_search/public/applications/index.tsx b/x-pack/plugins/enterprise_search/public/applications/index.tsx
index ac6d01f886175..b73128c701a41 100644
--- a/x-pack/plugins/enterprise_search/public/applications/index.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/index.tsx
@@ -6,23 +6,23 @@
import React from 'react';
import ReactDOM from 'react-dom';
-import { Router, Route, Redirect } from 'react-router-dom';
+import { Router } from 'react-router-dom';
+import { Observable } from 'rxjs';
import { I18nProvider } from '@kbn/i18n/react';
-import { CoreStart, AppMountParams, HttpHandler } from 'src/core/public';
+import { CoreStart, AppMountParameters, HttpSetup, ChromeBreadcrumb } from 'src/core/public';
import { ClientConfigType, PluginsSetup } from '../plugin';
-import { TSetBreadcrumbs } from './shared/kibana_breadcrumbs';
-import { ILicense } from '../../../../licensing/public';
+import { ILicense } from '../../../licensing/public';
import { LicenseProvider } from './shared/licensing';
export interface IKibanaContext {
enterpriseSearchUrl?: string;
- http(): HttpHandler;
- setBreadCrumbs(): TSetBreadcrumbs;
+ http: HttpSetup;
+ setBreadcrumbs(crumbs: ChromeBreadcrumb[]): void;
license$: Observable;
}
-export const KibanaContext = React.createContext();
+export const KibanaContext = React.createContext({});
/**
* This file serves as a reusable wrapper to share Kibana-level context and other helpers
@@ -31,9 +31,9 @@ export const KibanaContext = React.createContext();
*/
export const renderApp = (
- App: React.Element,
+ App: React.FC,
core: CoreStart,
- params: AppMountParams,
+ params: AppMountParameters,
config: ClientConfigType,
plugins: PluginsSetup
) => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts
index b07aacf443abb..5a5cce6ec23b6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.test.ts
@@ -7,7 +7,8 @@
import { generateBreadcrumb } from './generate_breadcrumbs';
import { appSearchBreadcrumbs, enterpriseSearchBreadcrumbs } from './';
-import { mockHistory } from '../../__mocks__';
+import { mockHistory as mockHistoryUntyped } from '../../__mocks__';
+const mockHistory = mockHistoryUntyped as any;
jest.mock('../react_router_helpers', () => ({ letBrowserHandleEvent: jest.fn(() => false) }));
import { letBrowserHandleEvent } from '../react_router_helpers';
@@ -31,7 +32,7 @@ describe('generateBreadcrumb', () => {
});
it('prevents default navigation and uses React Router history on click', () => {
- const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory });
+ const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory }) as any;
const event = { preventDefault: jest.fn() };
breadcrumb.onClick(event);
@@ -40,9 +41,9 @@ describe('generateBreadcrumb', () => {
});
it('does not prevents default browser behavior on new tab/window clicks', () => {
- const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory });
+ const breadcrumb = generateBreadcrumb({ text: '', path: '/', history: mockHistory }) as any;
- letBrowserHandleEvent.mockImplementationOnce(() => true);
+ (letBrowserHandleEvent as jest.Mock).mockImplementationOnce(() => true);
breadcrumb.onClick();
expect(mockHistory.push).not.toHaveBeenCalled();
@@ -103,19 +104,19 @@ describe('enterpriseSearchBreadcrumbs', () => {
describe('links', () => {
const eventMock = {
preventDefault: jest.fn(),
- };
+ } as any;
it('has Enterprise Search text first', () => {
expect(subject()[0].onClick).toBeUndefined();
});
it('has a link to page 1 second', () => {
- subject()[1].onClick(eventMock);
+ (subject()[1] as any).onClick(eventMock);
expect(mockHistory.push).toHaveBeenCalledWith('/page1');
});
it('has a link to page 2 last', () => {
- subject()[2].onClick(eventMock);
+ (subject()[2] as any).onClick(eventMock);
expect(mockHistory.push).toHaveBeenCalledWith('/page2');
});
});
@@ -136,7 +137,7 @@ describe('appSearchBreadcrumbs', () => {
beforeEach(() => {
jest.clearAllMocks();
mockHistory.createHref.mockImplementation(
- ({ pathname }) => `/enterprise_search/app_search${pathname}`
+ ({ pathname }: any) => `/enterprise_search/app_search${pathname}`
);
});
@@ -181,24 +182,24 @@ describe('appSearchBreadcrumbs', () => {
describe('links', () => {
const eventMock = {
preventDefault: jest.fn(),
- };
+ } as any;
it('has Enterprise Search text first', () => {
expect(subject()[0].onClick).toBeUndefined();
});
it('has a link to App Search second', () => {
- subject()[1].onClick(eventMock);
+ (subject()[1] as any).onClick(eventMock);
expect(mockHistory.push).toHaveBeenCalledWith('/');
});
it('has a link to page 1 third', () => {
- subject()[2].onClick(eventMock);
+ (subject()[2] as any).onClick(eventMock);
expect(mockHistory.push).toHaveBeenCalledWith('/page1');
});
it('has a link to page 2 last', () => {
- subject()[3].onClick(eventMock);
+ (subject()[3] as any).onClick(eventMock);
expect(mockHistory.push).toHaveBeenCalledWith('/page2');
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts
index 659a113dc31de..0e1bb796cbf2e 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/generate_breadcrumbs.ts
@@ -21,7 +21,7 @@ interface IGenerateBreadcrumbProps {
}
export const generateBreadcrumb = ({ text, path, history }: IGenerateBreadcrumbProps) => {
- const breadcrumb = { text };
+ const breadcrumb = { text } as EuiBreadcrumb;
if (path && history) {
breadcrumb.href = history.createHref({ pathname: path });
@@ -39,13 +39,15 @@ export const generateBreadcrumb = ({ text, path, history }: IGenerateBreadcrumbP
* Product-specific breadcrumb helpers
*/
-type TBreadcrumbs = EuiBreadcrumb[] | [];
+export type TBreadcrumbs = IGenerateBreadcrumbProps[];
export const enterpriseSearchBreadcrumbs = (history: History) => (
breadcrumbs: TBreadcrumbs = []
) => [
generateBreadcrumb({ text: 'Enterprise Search' }),
- ...breadcrumbs.map(({ text, path }) => generateBreadcrumb({ text, path, history })),
+ ...breadcrumbs.map(({ text, path }: IGenerateBreadcrumbProps) =>
+ generateBreadcrumb({ text, path, history })
+ ),
];
export const appSearchBreadcrumbs = (history: History) => (breadcrumbs: TBreadcrumbs = []) =>
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx
index aeaa38a5ad38f..974ca54277c51 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.test.tsx
@@ -14,16 +14,16 @@ import { appSearchBreadcrumbs, SetAppSearchBreadcrumbs } from './';
describe('SetAppSearchBreadcrumbs', () => {
const setBreadcrumbs = jest.fn();
- const builtBreadcrumbs = [];
+ const builtBreadcrumbs = [] as any;
const appSearchBreadCrumbsInnerCall = jest.fn().mockReturnValue(builtBreadcrumbs);
const appSearchBreadCrumbsOuterCall = jest.fn().mockReturnValue(appSearchBreadCrumbsInnerCall);
- appSearchBreadcrumbs.mockImplementation(appSearchBreadCrumbsOuterCall);
+ (appSearchBreadcrumbs as jest.Mock).mockImplementation(appSearchBreadCrumbsOuterCall);
afterEach(() => {
jest.clearAllMocks();
});
- const mountSetAppSearchBreadcrumbs = (props) => {
+ const mountSetAppSearchBreadcrumbs = (props: any) => {
return mountWithKibanaContext(, {
http: {},
enterpriseSearchUrl: 'http://localhost:3002',
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx
index aaa54febcc20b..ad3cd65c09516 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/kibana_breadcrumbs/set_breadcrumbs.tsx
@@ -8,7 +8,7 @@ import React, { useContext, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Breadcrumb as EuiBreadcrumb } from '@elastic/eui';
import { KibanaContext, IKibanaContext } from '../../index';
-import { appSearchBreadcrumbs } from './generate_breadcrumbs';
+import { appSearchBreadcrumbs, TBreadcrumbs } from './generate_breadcrumbs';
/**
* Small on-mount helper for setting Kibana's chrome breadcrumbs on any App Search view
@@ -17,20 +17,27 @@ import { appSearchBreadcrumbs } from './generate_breadcrumbs';
export type TSetBreadcrumbs = (breadcrumbs: EuiBreadcrumb[]) => void;
-interface ISetBreadcrumbsProps {
+interface IBreadcrumbProps {
text: string;
- isRoot?: boolean;
+ isRoot?: never;
+}
+interface IRootBreadcrumbProps {
+ isRoot: true;
+ text?: never;
}
-export const SetAppSearchBreadcrumbs: React.FC = ({ text, isRoot }) => {
+export const SetAppSearchBreadcrumbs: React.FC = ({
+ text,
+ isRoot,
+}) => {
const history = useHistory();
const { setBreadcrumbs } = useContext(KibanaContext) as IKibanaContext;
const crumb = isRoot ? [] : [{ text, path: history.location.pathname }];
useEffect(() => {
- setBreadcrumbs(appSearchBreadcrumbs(history)(crumb));
- }, []); // eslint-disable-line react-hooks/exhaustive-deps
+ setBreadcrumbs(appSearchBreadcrumbs(history)(crumb as TBreadcrumbs | []));
+ }, []);
return null;
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts
index e21bf004b39a2..ad134e7d36b10 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.test.ts
@@ -8,26 +8,26 @@ import { hasPlatinumLicense } from './license_checks';
describe('hasPlatinumLicense', () => {
it('is true for platinum licenses', () => {
- expect(hasPlatinumLicense({ isActive: true, type: 'platinum' })).toEqual(true);
+ expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true);
});
it('is true for enterprise licenses', () => {
- expect(hasPlatinumLicense({ isActive: true, type: 'enterprise' })).toEqual(true);
+ expect(hasPlatinumLicense({ isActive: true, type: 'enterprise' } as any)).toEqual(true);
});
it('is true for trial licenses', () => {
- expect(hasPlatinumLicense({ isActive: true, type: 'platinum' })).toEqual(true);
+ expect(hasPlatinumLicense({ isActive: true, type: 'platinum' } as any)).toEqual(true);
});
it('is false if the current license is expired', () => {
- expect(hasPlatinumLicense({ isActive: false, type: 'platinum' })).toEqual(false);
- expect(hasPlatinumLicense({ isActive: false, type: 'enterprise' })).toEqual(false);
- expect(hasPlatinumLicense({ isActive: false, type: 'trial' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'platinum' } as any)).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'enterprise' } as any)).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'trial' } as any)).toEqual(false);
});
it('is false for licenses below platinum', () => {
- expect(hasPlatinumLicense({ isActive: true, type: 'basic' })).toEqual(false);
- expect(hasPlatinumLicense({ isActive: false, type: 'standard' })).toEqual(false);
- expect(hasPlatinumLicense({ isActive: true, type: 'gold' })).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: true, type: 'basic' } as any)).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: false, type: 'standard' } as any)).toEqual(false);
+ expect(hasPlatinumLicense({ isActive: true, type: 'gold' } as any)).toEqual(false);
});
});
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts
index 7d0de8a093b31..363ae39ab0da4 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_checks.ts
@@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { ILicense } from '../../../../../../licensing/public';
+import { ILicense } from '../../../../../licensing/public';
-export const hasPlatinumLicense = (license: ILicenseContext) => {
- return license?.isActive && ['platinum', 'enterprise', 'trial'].includes(license?.type);
+export const hasPlatinumLicense = (license: ILicense) => {
+ return license?.isActive && ['platinum', 'enterprise', 'trial'].includes(license?.type as string);
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx
index 01d976bf49c19..c65474ec1f590 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.test.tsx
@@ -10,9 +10,9 @@ import { mountWithContext } from '../../__mocks__';
import { LicenseContext, ILicenseContext } from './';
describe('LicenseProvider', () => {
- const MockComponent: React.FC<> = () => {
+ const MockComponent: React.FC = () => {
const { license } = useContext(LicenseContext) as ILicenseContext;
- return {license.type}
;
+ return {license?.type}
;
};
it('renders children', () => {
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx
index c8295196aedb5..dec9cdac892f6 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/licensing/license_context.tsx
@@ -9,15 +9,15 @@ import useObservable from 'react-use/lib/useObservable';
import { KibanaContext, IKibanaContext } from '../../';
-import { ILicense } from '../../../../licensing/public';
+import { ILicense } from '../../../../../licensing/public';
export interface ILicenseContext {
- license?: ILicense;
+ license: ILicense;
}
-export const LicenseContext = React.createContext();
+export const LicenseContext = React.createContext({});
-export const LicenseProvider: React.FC<> = ({ children }) => {
+export const LicenseProvider: React.FC = ({ children }) => {
// Listen for changes to license subscription
const { license$ } = useContext(KibanaContext) as IKibanaContext;
const license = useObservable(license$);
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx
index eb9b9f3e35e06..7d4c068b21155 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.test.tsx
@@ -5,7 +5,7 @@
*/
import React from 'react';
-import { shallow } from 'enzyme';
+import { shallow, mount } from 'enzyme';
import { EuiLink, EuiButton } from '@elastic/eui';
import '../../__mocks__/react_router_history.mock';
@@ -25,23 +25,21 @@ describe('EUI & React Router Component Helpers', () => {
});
it('renders an EuiButton', () => {
- const wrapper = shallow()
- .find(EuiReactRouterLink)
- .dive();
+ const wrapper = shallow();
expect(wrapper.find(EuiButton)).toHaveLength(1);
});
it('passes down all ...rest props', () => {
- const wrapper = shallow();
+ const wrapper = shallow();
const link = wrapper.find(EuiLink);
- expect(link.prop('disabled')).toEqual(true);
+ expect(link.prop('external')).toEqual(true);
expect(link.prop('data-test-subj')).toEqual('foo');
});
it('renders with the correct href and onClick props', () => {
- const wrapper = shallow();
+ const wrapper = mount();
const link = wrapper.find(EuiLink);
expect(link.prop('onClick')).toBeInstanceOf(Function);
@@ -51,7 +49,7 @@ describe('EUI & React Router Component Helpers', () => {
describe('onClick', () => {
it('prevents default navigation and uses React Router history', () => {
- const wrapper = shallow();
+ const wrapper = mount();
const simulatedEvent = {
button: 0,
@@ -65,7 +63,7 @@ describe('EUI & React Router Component Helpers', () => {
});
it('does not prevent default browser behavior on new tab/window clicks', () => {
- const wrapper = shallow();
+ const wrapper = mount();
const simulatedEvent = {
shiftKey: true,
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx
index 3c410584cc49d..f486e432bae76 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/eui_link.tsx
@@ -6,7 +6,7 @@
import React from 'react';
import { useHistory } from 'react-router-dom';
-import { EuiLink, EuiButton } from '@elastic/eui';
+import { EuiLink, EuiButton, EuiButtonProps, EuiLinkAnchorProps } from '@elastic/eui';
import { letBrowserHandleEvent } from './link_events';
@@ -19,13 +19,12 @@ import { letBrowserHandleEvent } from './link_events';
interface IEuiReactRouterProps {
to: string;
- isButton?: boolean;
}
-export const EuiReactRouterLink: React.FC = ({ to, isButton, ...rest }) => {
+export const EuiReactRouterHelper: React.FC = ({ to, children }) => {
const history = useHistory();
- const onClick = (event) => {
+ const onClick = (event: React.MouseEvent) => {
if (letBrowserHandleEvent(event)) return;
// Prevent regular link behavior, which causes a browser refresh.
@@ -38,10 +37,21 @@ export const EuiReactRouterLink: React.FC = ({ to, isButto
// Generate the correct link href (with basename etc. accounted for)
const href = history.createHref({ pathname: to });
- const props = { ...rest, href, onClick };
- return isButton ? : ;
+ const reactRouterProps = { href, onClick };
+ return React.cloneElement(children as React.ReactElement, reactRouterProps);
};
-export const EuiReactRouterButton: React.FC = (props) => (
-
+type TEuiReactRouterLinkProps = EuiLinkAnchorProps & IEuiReactRouterProps;
+type TEuiReactRouterButtonProps = EuiButtonProps & IEuiReactRouterProps;
+
+export const EuiReactRouterLink: React.FC = ({ to, ...rest }) => (
+
+
+
+);
+
+export const EuiReactRouterButton: React.FC = ({ to, ...rest }) => (
+
+
+
);
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts
index 0845e5562776b..3682946b63a13 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.test.ts
@@ -17,7 +17,7 @@ describe('letBrowserHandleEvent', () => {
target: {
getAttribute: () => '_self',
},
- };
+ } as any;
describe('the browser should handle the link when', () => {
it('default is prevented', () => {
@@ -95,7 +95,7 @@ describe('letBrowserHandleEvent', () => {
});
});
-const targetValue = (value) => {
+const targetValue = (value: string | null) => {
return {
getAttribute: () => value,
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
index 67e987623c2c1..93da2ab71d952 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/react_router_helpers/link_events.ts
@@ -4,14 +4,14 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { SyntheticEvent } from 'react';
+import { MouseEvent } from 'react';
/**
* Helper functions for determining which events we should
* let browsers handle natively, e.g. new tabs/windows
*/
-type THandleEvent = (event: SyntheticEvent) => boolean;
+type THandleEvent = (event: MouseEvent) => boolean;
export const letBrowserHandleEvent: THandleEvent = (event) =>
event.defaultPrevented ||
@@ -25,6 +25,7 @@ const isModifiedEvent: THandleEvent = (event) =>
const isLeftClickEvent: THandleEvent = (event) => event.button === 0;
const isTargetBlank: THandleEvent = (event) => {
- const target = event.target.getAttribute('target');
+ const element = event.target as HTMLElement;
+ const target = element.getAttribute('target');
return !!target && target !== '_self';
};
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx
index da8fd25b9194b..e08fe6c06b0f1 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.test.tsx
@@ -33,18 +33,19 @@ describe('Shared Telemetry Helpers', () => {
});
it('throws an error if the telemetry endpoint fails', () => {
- const httpRejectMock = { put: () => Promise.reject() };
+ const httpRejectMock = sendTelemetry({
+ http: { put: () => Promise.reject() },
+ } as any);
- expect(sendTelemetry({ http: httpRejectMock })).rejects.toThrow('Unable to send telemetry');
+ expect(httpRejectMock).rejects.toThrow('Unable to send telemetry');
});
});
describe('React component helpers', () => {
it('SendAppSearchTelemetry component', () => {
- const wrapper = mountWithKibanaContext(
- ,
- { http: httpMock }
- );
+ mountWithKibanaContext(, {
+ http: httpMock,
+ });
expect(httpMock.put).toHaveBeenCalledWith('/api/app_search/telemetry', {
headers: { 'content-type': 'application/json; charset=utf-8' },
diff --git a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx
index 00c521303d269..0be26b2bf0459 100644
--- a/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx
+++ b/x-pack/plugins/enterprise_search/public/applications/shared/telemetry/send_telemetry.tsx
@@ -6,7 +6,7 @@
import React, { useContext, useEffect } from 'react';
-import { HttpHandler } from 'src/core/public';
+import { HttpSetup } from 'src/core/public';
import { KibanaContext, IKibanaContext } from '../../index';
interface ISendTelemetryProps {
@@ -15,7 +15,7 @@ interface ISendTelemetryProps {
}
interface ISendTelemetry extends ISendTelemetryProps {
- http(): HttpHandler;
+ http: HttpSetup;
product: 'app_search' | 'workplace_search' | 'enterprise_search';
}
diff --git a/x-pack/plugins/enterprise_search/public/plugin.ts b/x-pack/plugins/enterprise_search/public/plugin.ts
index 5863df5ccba25..1ebfdd779a791 100644
--- a/x-pack/plugins/enterprise_search/public/plugin.ts
+++ b/x-pack/plugins/enterprise_search/public/plugin.ts
@@ -32,7 +32,7 @@ export interface PluginsSetup {
export class EnterpriseSearchPlugin implements Plugin {
private config: ClientConfigType;
- constructor(private readonly initializerContext: PluginInitializerContext) {
+ constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.get();
}
diff --git a/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.test.ts
index 4be2c220024bc..9e82a7f8da9ee 100644
--- a/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.test.ts
+++ b/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.test.ts
@@ -17,7 +17,7 @@ describe('App Search Telemetry Usage Collector', () => {
const usageCollectionMock = {
makeUsageCollector: makeUsageCollectorStub,
registerCollector: registerStub,
- };
+ } as any;
const savedObjectsRepoStub = {
get: () => ({
@@ -35,7 +35,7 @@ describe('App Search Telemetry Usage Collector', () => {
};
const savedObjectsMock = {
createInternalRepository: jest.fn(() => savedObjectsRepoStub),
- };
+ } as any;
beforeEach(() => {
jest.clearAllMocks();
@@ -48,6 +48,7 @@ describe('App Search Telemetry Usage Collector', () => {
expect(registerStub).toHaveBeenCalledTimes(1);
expect(makeUsageCollectorStub).toHaveBeenCalledTimes(1);
expect(makeUsageCollectorStub.mock.calls[0][0].type).toBe('app_search');
+ expect(makeUsageCollectorStub.mock.calls[0][0].isReady()).toBe(true);
});
});
@@ -74,7 +75,7 @@ describe('App Search Telemetry Usage Collector', () => {
});
it('should not error & should return a default telemetry object if no saved data exists', async () => {
- const emptySavedObjectsMock = { createInternalRepository: () => ({}) };
+ const emptySavedObjectsMock = { createInternalRepository: () => ({}) } as any;
registerTelemetryUsageCollector(usageCollectionMock, emptySavedObjectsMock);
const savedObjectsCounts = await makeUsageCollectorStub.mock.calls[0][0].fetch();
diff --git a/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.ts
index 12b5a165bf1ac..2a396ead2f718 100644
--- a/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.ts
+++ b/x-pack/plugins/enterprise_search/server/collectors/app_search/telemetry.ts
@@ -5,7 +5,11 @@
*/
import { set } from 'lodash';
-import { ISavedObjectsRepository, SavedObjectsServiceStart } from 'src/core/server';
+import {
+ ISavedObjectsRepository,
+ SavedObjectsServiceStart,
+ SavedObjectAttributes,
+} from 'src/core/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { AS_TELEMETRY_NAME, ITelemetrySavedObject } from '../../saved_objects/app_search/telemetry';
@@ -21,6 +25,7 @@ export const registerTelemetryUsageCollector = (
const telemetryUsageCollector = usageCollection.makeUsageCollector({
type: 'app_search',
fetch: async () => fetchTelemetryMetrics(savedObjects),
+ isReady: () => true,
});
usageCollection.registerCollector(telemetryUsageCollector);
};
@@ -31,7 +36,9 @@ export const registerTelemetryUsageCollector = (
const fetchTelemetryMetrics = async (savedObjects: SavedObjectsServiceStart) => {
const savedObjectsRepository = savedObjects.createInternalRepository();
- const savedObjectAttributes = await getSavedObjectAttributesFromRepo(savedObjectsRepository);
+ const savedObjectAttributes = (await getSavedObjectAttributesFromRepo(
+ savedObjectsRepository
+ )) as SavedObjectAttributes;
const defaultTelemetrySavedObject: ITelemetrySavedObject = {
ui_viewed: {
@@ -68,10 +75,6 @@ const fetchTelemetryMetrics = async (savedObjects: SavedObjectsServiceStart) =>
* Helper function - fetches saved objects attributes
*/
-interface ISavedObjectAttributes {
- [key: string]: any;
-}
-
const getSavedObjectAttributesFromRepo = async (
savedObjectsRepository: ISavedObjectsRepository
) => {
diff --git a/x-pack/plugins/enterprise_search/server/plugin.ts b/x-pack/plugins/enterprise_search/server/plugin.ts
index 077d900a8d8cf..a8430ad8f56af 100644
--- a/x-pack/plugins/enterprise_search/server/plugin.ts
+++ b/x-pack/plugins/enterprise_search/server/plugin.ts
@@ -12,6 +12,7 @@ import {
CoreSetup,
Logger,
SavedObjectsServiceStart,
+ IRouter,
} from 'src/core/server';
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
@@ -28,10 +29,16 @@ export interface ServerConfigType {
host?: string;
}
+export interface IRouteDependencies {
+ router: IRouter;
+ config: ServerConfigType;
+ log: Logger;
+ getSavedObjectsService?(): SavedObjectsServiceStart;
+}
+
export class EnterpriseSearchPlugin implements Plugin {
private config: Observable;
private logger: Logger;
- private savedObjects?: SavedObjectsServiceStart;
constructor(initializerContext: PluginInitializerContext) {
this.config = initializerContext.config.create();
diff --git a/x-pack/plugins/enterprise_search/server/routes/__mocks__/router.mock.ts b/x-pack/plugins/enterprise_search/server/routes/__mocks__/router.mock.ts
index 1cec5da055140..332d1ad1062f2 100644
--- a/x-pack/plugins/enterprise_search/server/routes/__mocks__/router.mock.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/__mocks__/router.mock.ts
@@ -5,7 +5,12 @@
*/
import { httpServiceMock, httpServerMock } from 'src/core/server/mocks';
-import { IRouter, RequestHandlerContext, RouteValidatorConfig } from 'src/core/server';
+import {
+ IRouter,
+ KibanaRequest,
+ RequestHandlerContext,
+ RouteValidatorConfig,
+} from 'src/core/server';
/**
* Test helper that mocks Kibana's router and DRYs out various helper (callRoute, schema validation)
@@ -14,13 +19,24 @@ import { IRouter, RequestHandlerContext, RouteValidatorConfig } from 'src/core/s
type methodType = 'get' | 'post' | 'put' | 'patch' | 'delete';
type payloadType = 'params' | 'query' | 'body';
+interface IMockRouterProps {
+ method: methodType;
+ payload: payloadType;
+}
+interface IMockRouterRequest {
+ body?: object;
+ query?: object;
+ params?: object;
+}
+type TMockRouterRequest = KibanaRequest | IMockRouterRequest;
+
export class MockRouter {
- public router: jest.Mocked;
+ public router!: jest.Mocked;
public method: methodType;
public payload: payloadType;
public response = httpServerMock.createResponseFactory();
- private constructor({ method, payload }) {
+ constructor({ method, payload }: IMockRouterProps) {
this.createRouter();
this.method = method;
this.payload = payload;
@@ -30,29 +46,32 @@ export class MockRouter {
this.router = httpServiceMock.createRouter();
};
- public callRoute = async (request) => {
- const [_, handler] = this.router[this.method].mock.calls[0];
+ public callRoute = async (request: TMockRouterRequest) => {
+ const [, handler] = this.router[this.method].mock.calls[0];
const context = {} as jest.Mocked;
- await handler(context, httpServerMock.createKibanaRequest(request), this.response);
+ await handler(context, httpServerMock.createKibanaRequest(request as any), this.response);
};
/**
* Schema validation helpers
*/
- public validateRoute = (request) => {
+ public validateRoute = (request: TMockRouterRequest) => {
const [config] = this.router[this.method].mock.calls[0];
const validate = config.validate as RouteValidatorConfig<{}, {}, {}>;
- validate[this.payload].validate(request[this.payload]);
+ const payloadValidation = validate[this.payload] as { validate(request: KibanaRequest): void };
+ const payloadRequest = request[this.payload] as KibanaRequest;
+
+ payloadValidation.validate(payloadRequest);
};
- public shouldValidate = (request) => {
+ public shouldValidate = (request: TMockRouterRequest) => {
expect(() => this.validateRoute(request)).not.toThrow();
};
- public shouldThrow = (request) => {
+ public shouldThrow = (request: TMockRouterRequest) => {
expect(() => this.validateRoute(request)).toThrow();
};
}
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 722ad0d9269d3..c45514ae537fe 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
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { loggingServiceMock } from 'src/core/server/mocks';
+import { loggingSystemMock } from 'src/core/server/mocks';
import { MockRouter } from '../__mocks__/router.mock';
import { registerEnginesRoute } from './engines';
@@ -28,7 +28,7 @@ describe('engine routes', () => {
};
const mockRouter = new MockRouter({ method: 'get', payload: 'query' });
- const mockLogger = loggingServiceMock.create().get();
+ const mockLogger = loggingSystemMock.create().get();
beforeEach(() => {
jest.clearAllMocks();
@@ -172,7 +172,7 @@ describe('engine routes', () => {
return Promise.resolve(new Response(JSON.stringify(response)));
});
},
- andReturnInvalidData(response: object) {
+ andReturnInvalidData() {
fetchMock.mockImplementation((url: string, params: object) => {
expect(url).toEqual(expectedUrl);
expect(params).toEqual(expectedParams);
@@ -180,7 +180,7 @@ describe('engine routes', () => {
return Promise.resolve(new Response(JSON.stringify({ foo: 'bar' })));
});
},
- andReturnError(response: object) {
+ andReturnError() {
fetchMock.mockImplementation((url: string, params: object) => {
expect(url).toEqual(expectedUrl);
expect(params).toEqual(expectedParams);
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 ebe2252b24eef..ffc7a0228454f 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
@@ -8,9 +8,10 @@ import fetch from 'node-fetch';
import querystring from 'querystring';
import { schema } from '@kbn/config-schema';
+import { IRouteDependencies } from '../../plugin';
import { ENGINES_PAGE_SIZE } from '../../../common/constants';
-export function registerEnginesRoute({ router, config, log }) {
+export function registerEnginesRoute({ router, config, log }: IRouteDependencies) {
router.get(
{
path: '/api/app_search/engines',
@@ -23,7 +24,7 @@ export function registerEnginesRoute({ router, config, log }) {
},
async (context, request, response) => {
try {
- const appSearchUrl = config.host;
+ const appSearchUrl = config.host as string;
const { type, pageIndex } = request.query;
const params = querystring.stringify({
@@ -34,7 +35,7 @@ export function registerEnginesRoute({ router, config, log }) {
const url = `${encodeURI(appSearchUrl)}/as/engines/collection?${params}`;
const enginesResponse = await fetch(url, {
- headers: { Authorization: request.headers.authorization },
+ headers: { Authorization: request.headers.authorization as string },
});
if (enginesResponse.url.endsWith('/login')) {
@@ -58,7 +59,7 @@ export function registerEnginesRoute({ router, config, log }) {
}
} catch (e) {
log.error(`Cannot connect to App Search: ${e.toString()}`);
- if (e instanceof Error) log.debug(e.stack);
+ if (e instanceof Error) log.debug(e.stack as string);
return response.notFound({ body: 'cannot-connect' });
}
diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.test.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.test.ts
index 7644f3019de80..9e4ca2459ebd5 100644
--- a/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.test.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.test.ts
@@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
-import { loggingServiceMock, savedObjectsServiceMock } from 'src/core/server/mocks';
+import { loggingSystemMock, savedObjectsServiceMock } from 'src/core/server/mocks';
import { MockRouter } from '../__mocks__/router.mock';
import { registerTelemetryRoute } from './telemetry';
@@ -16,7 +16,7 @@ import { incrementUICounter } from '../../collectors/app_search/telemetry';
describe('App Search Telemetry API', () => {
const mockRouter = new MockRouter({ method: 'put', payload: 'body' });
- const mockLogger = loggingServiceMock.create().get();
+ const mockLogger = loggingSystemMock.create().get();
beforeEach(() => {
jest.clearAllMocks();
@@ -26,13 +26,13 @@ describe('App Search Telemetry API', () => {
router: mockRouter.router,
getSavedObjectsService: () => savedObjectsServiceMock.create(),
log: mockLogger,
- });
+ } as any);
});
describe('PUT /api/app_search/telemetry', () => {
it('increments the saved objects counter', async () => {
const successResponse = { success: true };
- incrementUICounter.mockImplementation(jest.fn(() => successResponse));
+ (incrementUICounter as jest.Mock).mockImplementation(jest.fn(() => successResponse));
await mockRouter.callRoute({ body: { action: 'viewed', metric: 'setup_guide' } });
@@ -45,7 +45,7 @@ describe('App Search Telemetry API', () => {
});
it('throws an error when incrementing fails', async () => {
- incrementUICounter.mockImplementation(jest.fn(() => Promise.reject('Failed')));
+ (incrementUICounter as jest.Mock).mockImplementation(jest.fn(() => Promise.reject('Failed')));
await mockRouter.callRoute({ body: { action: 'error', metric: 'error' } });
@@ -54,6 +54,20 @@ describe('App Search Telemetry API', () => {
expect(mockRouter.response.internalError).toHaveBeenCalled();
});
+ it('throws an error if the Saved Objects service is unavailable', async () => {
+ jest.clearAllMocks();
+ registerTelemetryRoute({
+ router: mockRouter.router,
+ getSavedObjectsService: null,
+ log: mockLogger,
+ } as any);
+ await mockRouter.callRoute({});
+
+ expect(incrementUICounter).not.toHaveBeenCalled();
+ expect(mockLogger.error).toHaveBeenCalled();
+ expect(mockRouter.response.internalError).toHaveBeenCalled();
+ });
+
describe('validates', () => {
it('correctly', () => {
const request = { body: { action: 'viewed', metric: 'setup_guide' } };
diff --git a/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.ts
index 6b7657a384e9f..4cc9b64adc092 100644
--- a/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.ts
+++ b/x-pack/plugins/enterprise_search/server/routes/app_search/telemetry.ts
@@ -6,9 +6,14 @@
import { schema } from '@kbn/config-schema';
+import { IRouteDependencies } from '../../plugin';
import { incrementUICounter } from '../../collectors/app_search/telemetry';
-export function registerTelemetryRoute({ router, getSavedObjectsService, log }) {
+export function registerTelemetryRoute({
+ router,
+ getSavedObjectsService,
+ log,
+}: IRouteDependencies) {
router.put(
{
path: '/api/app_search/telemetry',
@@ -27,6 +32,8 @@ export function registerTelemetryRoute({ router, getSavedObjectsService, log })
const { action, metric } = request.body;
try {
+ if (!getSavedObjectsService) throw new Error('Could not find Saved Objects service');
+
return response.ok({
body: await incrementUICounter({
savedObjects: getSavedObjectsService(),
diff --git a/x-pack/plugins/enterprise_search/server/saved_objects/app_search/telemetry.ts b/x-pack/plugins/enterprise_search/server/saved_objects/app_search/telemetry.ts
index 20c03b6aece8a..02bfe450ce7eb 100644
--- a/x-pack/plugins/enterprise_search/server/saved_objects/app_search/telemetry.ts
+++ b/x-pack/plugins/enterprise_search/server/saved_objects/app_search/telemetry.ts
@@ -27,7 +27,7 @@ export interface ITelemetrySavedObject {
export const appSearchTelemetryType: SavedObjectsType = {
name: AS_TELEMETRY_NAME,
hidden: false,
- namespaceAgnostic: true,
+ namespaceType: 'single',
mappings: {
properties: {
ui_viewed: {