[ML] Transforms: Fix error toasts. (#48509) (#48611)

Previously, error messages were just output within the toast as raw text which could result in overflows and overall unreadable results. This update fixes it by providing a "View details" button which opens a modal with the properly formatted error message. Plain text error message up to 140 character will still be shown in the toast itself.
This commit is contained in:
Walter Rafelsberger 2019-10-18 02:40:23 -07:00 committed by GitHub
parent 8da32357d2
commit bc375cacab
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 164 additions and 57 deletions

View file

@ -6,3 +6,4 @@
export { SectionError } from './section_error';
export { SectionLoading } from './section_loading';
export { ToastNotificationText } from './toast_notification_text';

View file

@ -0,0 +1,30 @@
/*
* 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 { cleanup, render } from 'react-testing-library';
import { ToastNotificationText } from './toast_notification_text';
describe('ToastNotificationText', () => {
afterEach(cleanup);
test('should render the text as plain text', () => {
const props = {
text: 'a short text message',
};
const { container } = render(<ToastNotificationText {...props} />);
expect(container.textContent).toBe('a short text message');
});
test('should render the text within a modal', () => {
const props = {
text:
'a text message that is longer than 140 characters. a text message that is longer than 140 characters. a text message that is longer than 140 characters. ',
};
const { container } = render(<ToastNotificationText {...props} />);
expect(container.textContent).toBe('View details');
});
});

View file

@ -0,0 +1,75 @@
/*
* 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, { FC } from 'react';
import {
EuiButtonEmpty,
EuiCodeBlock,
EuiModal,
EuiModalBody,
EuiModalFooter,
EuiModalHeader,
EuiModalHeaderTitle,
} from '@elastic/eui';
import { i18n } from '@kbn/i18n';
import { npStart } from 'ui/new_platform';
const MAX_SIMPLE_MESSAGE_LENGTH = 140;
export const ToastNotificationText: FC<{ text: any }> = ({ text }) => {
if (typeof text === 'string' && text.length <= MAX_SIMPLE_MESSAGE_LENGTH) {
return text;
}
if (
typeof text === 'object' &&
typeof text.message === 'string' &&
text.message.length <= MAX_SIMPLE_MESSAGE_LENGTH
) {
return text.message;
}
const formattedText = text.message ? text.message : JSON.stringify(text, null, 2);
const openModal = () => {
const modal = npStart.core.overlays.openModal(
<EuiModal onClose={() => modal.close()}>
<EuiModalHeader>
<EuiModalHeaderTitle>
{i18n.translate('xpack.transform.toastText.modalTitle', {
defaultMessage: 'Error details',
})}
</EuiModalHeaderTitle>
</EuiModalHeader>
<EuiModalBody>
<EuiCodeBlock language="json" fontSize="m" paddingSize="s" isCopyable>
{formattedText}
</EuiCodeBlock>
</EuiModalBody>
<EuiModalFooter>
<EuiButtonEmpty onClick={() => modal.close()}>
{i18n.translate('xpack.transform.toastText.closeModalButtonText', {
defaultMessage: 'Close',
})}
</EuiButtonEmpty>
</EuiModalFooter>
</EuiModal>
);
};
return (
<>
<EuiButtonEmpty onClick={openModal}>
{i18n.translate('xpack.transform.toastText.openModalButtonText', {
defaultMessage: 'View details',
})}
</EuiButtonEmpty>
</>
);
};

View file

@ -4,10 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React from 'react';
import { i18n } from '@kbn/i18n';
import { toastNotifications } from 'ui/notify';
import { TransformListRow, refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../common';
import { ToastNotificationText } from '../components';
import { useApi } from './use_api';
import { TransformEndpointRequest, TransformEndpointResult } from './use_api_types';
@ -46,11 +49,12 @@ export const useDeleteTransforms = () => {
refreshTransformList$.next(REFRESH_TRANSFORM_LIST_STATE.REFRESH);
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.transformList.deleteTransformGenericErrorMessage', {
defaultMessage: 'An error occurred calling the API endpoint to delete transforms.',
})
);
}),
text: <ToastNotificationText text={e} />,
});
}
};
};

View file

@ -29,6 +29,7 @@ import {
EuiText,
} from '@elastic/eui';
import { ToastNotificationText } from '../../../../components';
import { useApi } from '../../../../hooks/use_api';
import { isKibanaContextInitialized, KibanaContext } from '../../../../lib/kibana';
import { RedirectToTransformManagement } from '../../../../common/navigation';
@ -96,12 +97,13 @@ export const StepCreateForm: SFC<Props> = React.memo(
);
} catch (e) {
setCreated(false);
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', {
defaultMessage: 'An error occurred creating the transform {transformId}: {error}',
values: { transformId, error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepCreateForm.createTransformErrorMessage', {
defaultMessage: 'An error occurred creating the transform {transformId}:',
values: { transformId },
}),
text: <ToastNotificationText text={e} />,
});
return false;
}
@ -125,12 +127,13 @@ export const StepCreateForm: SFC<Props> = React.memo(
);
} catch (e) {
setStarted(false);
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', {
defaultMessage: 'An error occurred starting the transform {transformId}: {error}',
values: { transformId, error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepCreateForm.startTransformErrorMessage', {
defaultMessage: 'An error occurred starting the transform {transformId}:',
values: { transformId },
}),
text: <ToastNotificationText text={e} />,
});
}
}
@ -182,13 +185,14 @@ export const StepCreateForm: SFC<Props> = React.memo(
setIndexPatternId(id);
return true;
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepCreateForm.createIndexPatternErrorMessage', {
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepCreateForm.createIndexPatternErrorMessage', {
defaultMessage:
'An error occurred creating the Kibana index pattern {indexPatternName}: {error}',
values: { indexPatternName, error: JSON.stringify(e) },
})
);
'An error occurred creating the Kibana index pattern {indexPatternName}:',
values: { indexPatternName },
}),
text: <ToastNotificationText text={e} />,
});
return false;
}
};
@ -214,12 +218,12 @@ export const StepCreateForm: SFC<Props> = React.memo(
}
}
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', {
defaultMessage: 'An error occurred getting the progress percentage: {error}',
values: { error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepCreateForm.progressErrorMessage', {
defaultMessage: 'An error occurred getting the progress percentage:',
}),
text: <ToastNotificationText text={e} />,
});
clearInterval(interval);
}
}, PROGRESS_REFRESH_INTERVAL_MS);

View file

@ -15,6 +15,7 @@ import { EuiLink, EuiSwitch, EuiFieldText, EuiForm, EuiFormRow, EuiSelect } from
import { isKibanaContextInitialized, KibanaContext } from '../../../../lib/kibana';
import { isValidIndexName } from '../../../../../../common/utils/es_utils';
import { ToastNotificationText } from '../../../../components';
import { useApi } from '../../../../hooks/use_api';
import { isTransformIdValid, TransformId, TransformPivotConfig } from '../../../../common';
@ -86,35 +87,37 @@ export const StepDetailsForm: SFC<Props> = React.memo(({ overrides = {}, onChang
)
);
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', {
defaultMessage: 'An error occurred getting the existing transform IDs: {error}',
values: { error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingTransformList', {
defaultMessage: 'An error occurred getting the existing transform IDs:',
}),
text: <ToastNotificationText text={e} />,
});
}
try {
setIndexNames((await api.getIndices()).map(index => index.name));
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', {
defaultMessage: 'An error occurred getting the existing index names: {error}',
values: { error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexNames', {
defaultMessage: 'An error occurred getting the existing index names:',
}),
text: <ToastNotificationText text={e} />,
});
}
try {
setIndexPatternTitles(await kibanaContext.indexPatterns.getTitles());
} catch (e) {
toastNotifications.addDanger(
i18n.translate('xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles', {
defaultMessage:
'An error occurred getting the existing index pattern titles: {error}',
values: { error: JSON.stringify(e) },
})
);
toastNotifications.addDanger({
title: i18n.translate(
'xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles',
{
defaultMessage: 'An error occurred getting the existing index pattern titles:',
}
),
text: <ToastNotificationText text={e} />,
});
}
}
})();

View file

@ -6985,21 +6985,16 @@
"xpack.transform.groupByLabelForm.editIntervalAriaLabel": "間隔を編集",
"xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton": "クリップボードにコピー",
"xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription": "ジョブを作成する Kibana 開発コンソールのコマンドをクリップボードにコピーします。",
"xpack.transform.stepCreateForm.createIndexPatternErrorMessage": "Kibana インデックスパターン {indexPatternName} の作成中にエラーが発生しました: {error}",
"xpack.transform.stepCreateForm.createIndexPatternLabel": "インデックスパターンを作成",
"xpack.transform.stepCreateForm.createTransformErrorMessage": "データフレームジョブ {transformId} の作成中にエラーが発生しました: {error}",
"xpack.transform.stepCreateForm.createTransformSuccessMessage": "データフレームジョブ {transformId} が作成されました",
"xpack.transform.stepCreateForm.creatingIndexPatternMessage": "Kibana インデックスパターンを作成中…",
"xpack.transform.stepCreateForm.discoverCardDescription": "ディスカバリでデータフレームピボットを閲覧します。",
"xpack.transform.stepCreateForm.discoverCardTitle": "ディスカバー",
"xpack.transform.stepCreateForm.transformListCardDescription": "データフレームジョブの管理ページに戻ります。",
"xpack.transform.stepCreateForm.transformListCardTitle": "データフレームジョブ",
"xpack.transform.stepCreateForm.progressErrorMessage": "進捗パーセンテージの取得中にエラーが発生しました: {error}",
"xpack.transform.stepCreateForm.progressTitle": "進捗",
"xpack.transform.stepCreateForm.createIndexPatternSuccessMessage": "Kibana インデックスパターン {indexPatternName} が作成されました",
"xpack.transform.stepCreateForm.startTransformErrorMessage": "データフレームジョブ {transformId} の開始中にエラーが発生しました: {error}",
"xpack.transform.stepCreateForm.startTransformSuccessMessage": "データフレームジョブ {transformId} が開始しました",
"xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles": "既存のインデックスパターンのタイトルの取得中にエラーが発生しました: {error}",
"xpack.transform.stepDetailsForm.indexPatternTitleError": "このタイトルのインデックスパターンが既に存在します。",
"xpack.transform.stepDetailsForm.transformIdInputAriaLabel": "固有のジョブ ID を選択してください。",
"xpack.transform.stepDetailsForm.transformIdLabel": "ジョブ ID",

View file

@ -7143,21 +7143,16 @@
"xpack.transform.groupByLabelForm.editIntervalAriaLabel": "编辑时间间隔",
"xpack.transform.stepCreateForm.copyTransformConfigToClipboardButton": "复制到剪贴板",
"xpack.transform.stepCreateForm.copyTransformConfigToClipboardDescription": "将用于创建作业的 Kibana 开发控制台命令复制到剪贴板。",
"xpack.transform.stepCreateForm.createIndexPatternErrorMessage": "创建 Kibana 索引模式 {indexPatternName} 时发生错误:{error}",
"xpack.transform.stepCreateForm.createIndexPatternLabel": "创建索引模式",
"xpack.transform.stepCreateForm.createTransformErrorMessage": "创建数据帧作业 {transformId} 时发生错误:{error}",
"xpack.transform.stepCreateForm.createTransformSuccessMessage": "数据帧作业 {transformId} 创建成功。",
"xpack.transform.stepCreateForm.creatingIndexPatternMessage": "正在创建 Kibana 索引模式......",
"xpack.transform.stepCreateForm.discoverCardDescription": "使用 Discover 浏览数据帧透视表。",
"xpack.transform.stepCreateForm.discoverCardTitle": "Discover",
"xpack.transform.stepCreateForm.transformListCardDescription": "返回数据帧作业管理页面。",
"xpack.transform.stepCreateForm.transformListCardTitle": "数据帧作业",
"xpack.transform.stepCreateForm.progressErrorMessage": "获取进度百分比时出错:{error}",
"xpack.transform.stepCreateForm.progressTitle": "进度",
"xpack.transform.stepCreateForm.createIndexPatternSuccessMessage": "Kibana 索引模式 {indexPatternName} 成功创建。",
"xpack.transform.stepCreateForm.startTransformErrorMessage": "启动数据帧作业 {transformId} 时发生错误:{error}",
"xpack.transform.stepCreateForm.startTransformSuccessMessage": "数据帧作业 {transformId} 启动成功。",
"xpack.transform.stepDetailsForm.errorGettingIndexPatternTitles": "获取现有索引名称时发生错误:{error}",
"xpack.transform.stepDetailsForm.indexPatternTitleError": "具有此名称的索引模式已存在。",
"xpack.transform.stepDetailsForm.transformIdInputAriaLabel": "选择唯一的作业 ID。",
"xpack.transform.stepDetailsForm.transformIdLabel": "作业 ID",