Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

[Enterprise Search] Show error panel on ELSER API call errors #156273

Merged
merged 10 commits into from
May 2, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const createTextExpansionModel = async (): Promise<CreateTextExpansionMod

export const CreateTextExpansionModelApiLogic = createApiLogic(
['create_text_expansion_model_api_logic'],
createTextExpansionModel
createTextExpansionModel,
{ showErrorFlash: false }
);

export type CreateTextExpansionModelApiLogicActions = Actions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export const fetchTextExpansionModelStatus = async () => {

export const FetchTextExpansionModelApiLogic = createApiLogic(
['fetch_text_expansion_model_api_logic'],
fetchTextExpansionModelStatus
fetchTextExpansionModelStatus,
{ showErrorFlash: false }
);

export type FetchTextExpansionModelApiLogicActions = Actions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const startTextExpansionModel = async (): Promise<StartTextExpansionModel

export const StartTextExpansionModelApiLogic = createApiLogic(
['start_text_expansion_model_api_logic'],
startTextExpansionModel
startTextExpansionModel,
{ showErrorFlash: false }
);

export type StartTextExpansionModelApiLogicActions = Actions<
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import { shallow } from 'enzyme';

import { EuiButton } from '@elastic/eui';

import { HttpError } from '../../../../../../../common/types/api';

import {
TextExpansionCallOut,
DeployModel,
Expand All @@ -22,6 +24,8 @@ import {
ModelStarted,
} from './text_expansion_callout';

import { TextExpansionErrors } from './text_expansion_errors';

jest.mock('./text_expansion_callout_data', () => ({
useTextExpansionCallOutData: jest.fn(() => ({
dismiss: jest.fn(),
Expand All @@ -33,6 +37,7 @@ jest.mock('./text_expansion_callout_data', () => ({
}));

const DEFAULT_VALUES = {
error: undefined,
isCreateButtonDisabled: false,
isModelDownloadInProgress: false,
isModelDownloaded: false,
Expand All @@ -45,6 +50,21 @@ describe('TextExpansionCallOut', () => {
jest.clearAllMocks();
setMockValues(DEFAULT_VALUES);
});
it('renders error panel instead of normal panel if there are some errors', () => {
setMockValues({
...DEFAULT_VALUES,
error: {
body: {
error: 'some-error',
message: 'some-error-message',
statusCode: 500,
},
} as HttpError,
});

const wrapper = shallow(<TextExpansionCallOut />);
expect(wrapper.find(TextExpansionErrors).length).toBe(1);
});
it('renders panel with deployment instructions if the model is not deployed', () => {
const wrapper = shallow(<TextExpansionCallOut />);
expect(wrapper.find(DeployModel).length).toBe(1);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,13 @@ import { FormattedMessage, FormattedHTMLMessage } from '@kbn/i18n-react';
import { docLinks } from '../../../../../shared/doc_links';
import { KibanaLogic } from '../../../../../shared/kibana';

import { CreateTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/create_text_expansion_model_api_logic';
import { FetchTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/fetch_text_expansion_model_api_logic';
import { StartTextExpansionModelApiLogic } from '../../../../api/ml_models/text_expansion/start_text_expansion_model_api_logic';

import { useTextExpansionCallOutData } from './text_expansion_callout_data';
import { TextExpansionCalloutLogic } from './text_expansion_callout_logic';
import { TextExpansionErrors } from './text_expansion_errors';

export interface TextExpansionCallOutState {
dismiss: () => void;
Expand Down Expand Up @@ -333,6 +338,19 @@ export const TextExpansionCallOut: React.FC<TextExpansionCallOutProps> = (props)
isModelStarted,
isStartButtonDisabled,
} = useValues(TextExpansionCalloutLogic);
const { error: createError } = useValues(CreateTextExpansionModelApiLogic);
demjened marked this conversation as resolved.
Show resolved Hide resolved
const { error: fetchError } = useValues(FetchTextExpansionModelApiLogic);
const { error: startError } = useValues(StartTextExpansionModelApiLogic);

if (createError !== undefined || fetchError !== undefined || startError !== undefined) {
return (
<TextExpansionErrors
createError={createError}
fetchError={fetchError}
startError={startError}
/>
);
}

if (!show) return null;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,23 @@ export const TextExpansionCalloutLogic = kea<
connect: {
actions: [
CreateTextExpansionModelApiLogic,
['makeRequest as createTextExpansionModel', 'apiSuccess as createTextExpansionModelSuccess'],
[
'makeRequest as createTextExpansionModel',
'apiSuccess as createTextExpansionModelSuccess',
'apiError as createTextExpansionModelError',
],
FetchTextExpansionModelApiLogic,
[
'makeRequest as fetchTextExpansionModel',
'apiSuccess as fetchTextExpansionModelSuccess',
'apiError as fetchTextExpansionModelError',
],
StartTextExpansionModelApiLogic,
['makeRequest as startTextExpansionModel', 'apiSuccess as startTextExpansionModelSuccess'],
[
'makeRequest as startTextExpansionModel',
'apiSuccess as startTextExpansionModelSuccess',
'apiError as startTextExpansionModelError',
],
],
values: [
CreateTextExpansionModelApiLogic,
Expand Down Expand Up @@ -169,7 +177,7 @@ export const TextExpansionCalloutLogic = kea<
selectors: ({ selectors }) => ({
isCreateButtonDisabled: [
() => [selectors.createTextExpansionModelStatus],
(status: Status) => status !== Status.IDLE,
(status: Status) => status !== Status.IDLE && status !== Status.ERROR,
],
isModelDownloadInProgress: [
() => [selectors.textExpansionModel],
Expand All @@ -195,7 +203,7 @@ export const TextExpansionCalloutLogic = kea<
],
isStartButtonDisabled: [
() => [selectors.startTextExpansionModelStatus],
(status: Status) => status !== Status.IDLE,
(status: Status) => status !== Status.IDLE && status !== Status.ERROR,
],
}),
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import { setMockValues } from '../../../../../__mocks__/kea_logic';

import React from 'react';

import { shallow } from 'enzyme';

import { EuiCallOut } from '@elastic/eui';

import { HttpError } from '../../../../../../../common/types/api';

import { TextExpansionErrors } from './text_expansion_errors';

describe('TextExpansionErrors', () => {
beforeEach(() => {
jest.clearAllMocks();
setMockValues({});
});
const error = {
body: {
error: 'some-error',
message: 'some-error-message',
statusCode: 500,
},
} as HttpError;
it('renders error panel if ELSER deployment fails', () => {
const wrapper = shallow(
<TextExpansionErrors createError={error} fetchError={undefined} startError={undefined} />
);
expect(wrapper.find(EuiCallOut).length).toBe(1);
expect(wrapper.find(EuiCallOut).prop('title')).toEqual('Error with ELSER deployment');
});
it('renders error panel if ELSER fetching fails', () => {
const wrapper = shallow(
<TextExpansionErrors createError={undefined} fetchError={error} startError={undefined} />
);
expect(wrapper.find(EuiCallOut).length).toBe(1);
expect(wrapper.find(EuiCallOut).prop('title')).toEqual('Error fetching ELSER model');
});
it('renders error panel if ELSER starting fails', () => {
const wrapper = shallow(
<TextExpansionErrors createError={undefined} fetchError={undefined} startError={error} />
);
expect(wrapper.find(EuiCallOut).length).toBe(1);
expect(wrapper.find(EuiCallOut).prop('title')).toEqual('Error starting ELSER deployment');
});
it('extracts and renders the error message', () => {
const wrapper = shallow(
<TextExpansionErrors createError={error} fetchError={undefined} startError={undefined} />
);
expect(wrapper.find(EuiCallOut).find('p').length).toBe(1);
expect(wrapper.find(EuiCallOut).find('p').text()).toEqual(error?.body?.message);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/

import React from 'react';

import { useValues } from 'kea';

import { EuiCallOut, EuiLink } from '@elastic/eui';

import { i18n } from '@kbn/i18n';

import { HttpError } from '../../../../../../../common/types/api';
import { getErrorsFromHttpResponse } from '../../../../../shared/flash_messages/handle_api_errors';
import { HttpLogic } from '../../../../../shared/http';

import { ML_NOTIFICATIONS_PATH } from '../../../../routes';

export const TextExpansionErrors = ({
demjened marked this conversation as resolved.
Show resolved Hide resolved
createError,
fetchError,
startError,
}: {
createError: HttpError | undefined;
fetchError: HttpError | undefined;
startError: HttpError | undefined;
}) => {
const { http } = useValues(HttpLogic);

// Extract the topmost error in precedence order
const error: HttpError | undefined = createError ?? startError ?? fetchError;
if (error === undefined) {
return null;
}
const topError = getErrorsFromHttpResponse(error)[0];

return (
<>
<EuiCallOut
color="danger"
iconType="error"
title={
createError !== undefined
? i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.title',
{
defaultMessage: 'Error with ELSER deployment',
}
)
: startError !== undefined
? i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.textExpansionStartError.title',
{
defaultMessage: 'Error starting ELSER deployment',
}
)
: i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.textExpansionFetchError.title',
{
defaultMessage: 'Error fetching ELSER model',
}
)
}
>
<p>{topError}</p>
<EuiLink href={http.basePath.prepend(ML_NOTIFICATIONS_PATH)} target="_blank">
{i18n.translate(
'xpack.enterpriseSearch.content.indices.pipelines.textExpansionCreateError.mlNotificationsLink',
{
defaultMessage: 'Machine Learning notifications',
}
)}
</EuiLink>
</EuiCallOut>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ export enum SearchApplicationContentTabs {
}

export const ML_MANAGE_TRAINED_MODELS_PATH = '/app/ml/trained_models';
export const ML_NOTIFICATIONS_PATH = '/app/ml/notifications';
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface Options {
isQueued?: boolean;
}

export const defaultErrorMessage = i18n.translate(
export const defaultErrorMessage: string = i18n.translate(
demjened marked this conversation as resolved.
Show resolved Hide resolved
'xpack.enterpriseSearch.shared.flashMessages.defaultErrorMessage',
{
defaultMessage: 'An unexpected error occurred',
Expand Down