mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Populate date fields for Transform (#108804)
* [ML] Add index pattern info & select control for date time * [ML] Update translations * [ML] Gracefully handle when index pattern is not available * [ML] Fix import * [ML] Handle when unmounted * [ML] Remove load index patterns because we don't really need it * [ML] Add error obj to error toasts * [ML] Update tests * [ML] Update hook Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
bbfad19051
commit
42acf39a70
9 changed files with 172 additions and 40 deletions
|
@ -42,8 +42,8 @@ export const useCloneAction = (forceDisable: boolean, transformNodes: number) =>
|
|||
toastNotifications.addDanger(
|
||||
i18n.translate('xpack.transform.clone.noIndexPatternErrorPromptText', {
|
||||
defaultMessage:
|
||||
'Unable to clone the transform . No index pattern exists for {indexPattern}.',
|
||||
values: { indexPattern: indexPatternTitle },
|
||||
'Unable to clone the transform {transformId}. No index pattern exists for {indexPattern}.',
|
||||
values: { indexPattern: indexPatternTitle, transformId: item.id },
|
||||
})
|
||||
);
|
||||
} else {
|
||||
|
@ -52,11 +52,11 @@ export const useCloneAction = (forceDisable: boolean, transformNodes: number) =>
|
|||
);
|
||||
}
|
||||
} catch (e) {
|
||||
toastNotifications.addDanger(
|
||||
i18n.translate('xpack.transform.clone.errorPromptText', {
|
||||
toastNotifications.addError(e, {
|
||||
title: i18n.translate('xpack.transform.clone.errorPromptText', {
|
||||
defaultMessage: 'An error occurred checking if source index pattern exists',
|
||||
})
|
||||
);
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
[
|
||||
|
|
|
@ -5,25 +5,63 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { useContext, useMemo, useState } from 'react';
|
||||
import React, { useCallback, useContext, useMemo, useState } from 'react';
|
||||
|
||||
import { TransformConfigUnion } from '../../../../../../common/types/transform';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { TransformListAction, TransformListRow } from '../../../../common';
|
||||
import { AuthorizationContext } from '../../../../lib/authorization';
|
||||
|
||||
import { editActionNameText, EditActionName } from './edit_action_name';
|
||||
import { useSearchItems } from '../../../../hooks/use_search_items';
|
||||
import { useAppDependencies, useToastNotifications } from '../../../../app_dependencies';
|
||||
import { TransformConfigUnion } from '../../../../../../common/types/transform';
|
||||
|
||||
export const useEditAction = (forceDisable: boolean, transformNodes: number) => {
|
||||
const { canCreateTransform } = useContext(AuthorizationContext).capabilities;
|
||||
|
||||
const [config, setConfig] = useState<TransformConfigUnion>();
|
||||
const [isFlyoutVisible, setIsFlyoutVisible] = useState(false);
|
||||
const [indexPatternId, setIndexPatternId] = useState<string | undefined>();
|
||||
|
||||
const closeFlyout = () => setIsFlyoutVisible(false);
|
||||
const showFlyout = (newConfig: TransformConfigUnion) => {
|
||||
setConfig(newConfig);
|
||||
setIsFlyoutVisible(true);
|
||||
};
|
||||
|
||||
const { getIndexPatternIdByTitle } = useSearchItems(undefined);
|
||||
const toastNotifications = useToastNotifications();
|
||||
const appDeps = useAppDependencies();
|
||||
const indexPatterns = appDeps.data.indexPatterns;
|
||||
|
||||
const clickHandler = useCallback(
|
||||
async (item: TransformListRow) => {
|
||||
try {
|
||||
const indexPatternTitle = Array.isArray(item.config.source.index)
|
||||
? item.config.source.index.join(',')
|
||||
: item.config.source.index;
|
||||
const currentIndexPatternId = getIndexPatternIdByTitle(indexPatternTitle);
|
||||
|
||||
if (currentIndexPatternId === undefined) {
|
||||
toastNotifications.addWarning(
|
||||
i18n.translate('xpack.transform.edit.noIndexPatternErrorPromptText', {
|
||||
defaultMessage:
|
||||
'Unable to get index pattern the transform {transformId}. No index pattern exists for {indexPattern}.',
|
||||
values: { indexPattern: indexPatternTitle, transformId: item.id },
|
||||
})
|
||||
);
|
||||
}
|
||||
setIndexPatternId(currentIndexPatternId);
|
||||
setConfig(item.config);
|
||||
setIsFlyoutVisible(true);
|
||||
} catch (e) {
|
||||
toastNotifications.addError(e, {
|
||||
title: i18n.translate('xpack.transform.edit.errorPromptText', {
|
||||
defaultMessage: 'An error occurred checking if source index pattern exists',
|
||||
}),
|
||||
});
|
||||
}
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
[indexPatterns, toastNotifications, getIndexPatternIdByTitle]
|
||||
);
|
||||
|
||||
const action: TransformListAction = useMemo(
|
||||
() => ({
|
||||
|
@ -32,10 +70,10 @@ export const useEditAction = (forceDisable: boolean, transformNodes: number) =>
|
|||
description: editActionNameText,
|
||||
icon: 'pencil',
|
||||
type: 'icon',
|
||||
onClick: (item: TransformListRow) => showFlyout(item.config),
|
||||
onClick: (item: TransformListRow) => clickHandler(item),
|
||||
'data-test-subj': 'transformActionEdit',
|
||||
}),
|
||||
[canCreateTransform, forceDisable, transformNodes]
|
||||
[canCreateTransform, clickHandler, forceDisable, transformNodes]
|
||||
);
|
||||
|
||||
return {
|
||||
|
@ -43,6 +81,6 @@ export const useEditAction = (forceDisable: boolean, transformNodes: number) =>
|
|||
config,
|
||||
closeFlyout,
|
||||
isFlyoutVisible,
|
||||
showFlyout,
|
||||
indexPatternId,
|
||||
};
|
||||
};
|
||||
|
|
|
@ -30,7 +30,6 @@ import { getErrorMessage } from '../../../../../../common/utils/errors';
|
|||
|
||||
import { refreshTransformList$, REFRESH_TRANSFORM_LIST_STATE } from '../../../../common';
|
||||
import { useToastNotifications } from '../../../../app_dependencies';
|
||||
|
||||
import { useApi } from '../../../../hooks/use_api';
|
||||
|
||||
import { EditTransformFlyoutCallout } from './edit_transform_flyout_callout';
|
||||
|
@ -43,9 +42,14 @@ import {
|
|||
interface EditTransformFlyoutProps {
|
||||
closeFlyout: () => void;
|
||||
config: TransformConfigUnion;
|
||||
indexPatternId?: string;
|
||||
}
|
||||
|
||||
export const EditTransformFlyout: FC<EditTransformFlyoutProps> = ({ closeFlyout, config }) => {
|
||||
export const EditTransformFlyout: FC<EditTransformFlyoutProps> = ({
|
||||
closeFlyout,
|
||||
config,
|
||||
indexPatternId,
|
||||
}) => {
|
||||
const api = useApi();
|
||||
const toastNotifications = useToastNotifications();
|
||||
|
||||
|
@ -96,7 +100,10 @@ export const EditTransformFlyout: FC<EditTransformFlyoutProps> = ({ closeFlyout,
|
|||
</EuiTitle>
|
||||
</EuiFlyoutHeader>
|
||||
<EuiFlyoutBody banner={<EditTransformFlyoutCallout />}>
|
||||
<EditTransformFlyoutForm editTransformFlyout={[state, dispatch]} />
|
||||
<EditTransformFlyoutForm
|
||||
editTransformFlyout={[state, dispatch]}
|
||||
indexPatternId={indexPatternId}
|
||||
/>
|
||||
{errorMessage !== undefined && (
|
||||
<>
|
||||
<EuiSpacer size="m" />
|
||||
|
|
|
@ -5,23 +5,58 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC } from 'react';
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { EuiForm, EuiAccordion, EuiSpacer } from '@elastic/eui';
|
||||
import { EuiForm, EuiAccordion, EuiSpacer, EuiSelect, EuiFormRow } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { EditTransformFlyoutFormTextInput } from './edit_transform_flyout_form_text_input';
|
||||
import { UseEditTransformFlyoutReturnType } from './use_edit_transform_flyout';
|
||||
import { useAppDependencies } from '../../../../app_dependencies';
|
||||
import { KBN_FIELD_TYPES } from '../../../../../../../../../src/plugins/data/common';
|
||||
|
||||
interface EditTransformFlyoutFormProps {
|
||||
editTransformFlyout: UseEditTransformFlyoutReturnType;
|
||||
indexPatternId?: string;
|
||||
}
|
||||
|
||||
export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
||||
editTransformFlyout: [state, dispatch],
|
||||
indexPatternId,
|
||||
}) => {
|
||||
const formFields = state.formFields;
|
||||
const [dateFieldNames, setDateFieldNames] = useState<string[]>([]);
|
||||
|
||||
const appDeps = useAppDependencies();
|
||||
const indexPatternsClient = appDeps.data.indexPatterns;
|
||||
|
||||
useEffect(
|
||||
function getDateFields() {
|
||||
let unmounted = false;
|
||||
if (indexPatternId !== undefined) {
|
||||
indexPatternsClient.get(indexPatternId).then((indexPattern) => {
|
||||
if (indexPattern) {
|
||||
const dateTimeFields = indexPattern.fields
|
||||
.filter((f) => f.type === KBN_FIELD_TYPES.DATE)
|
||||
.map((f) => f.name)
|
||||
.sort();
|
||||
if (!unmounted) {
|
||||
setDateFieldNames(dateTimeFields);
|
||||
}
|
||||
}
|
||||
});
|
||||
return () => {
|
||||
unmounted = true;
|
||||
};
|
||||
}
|
||||
},
|
||||
[indexPatternId, indexPatternsClient]
|
||||
);
|
||||
|
||||
const retentionDateFieldOptions = useMemo(() => {
|
||||
return Array.isArray(dateFieldNames) ? dateFieldNames.map((text: string) => ({ text })) : [];
|
||||
}, [dateFieldNames]);
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
|
@ -112,19 +147,57 @@ export const EditTransformFlyoutForm: FC<EditTransformFlyoutFormProps> = ({
|
|||
paddingSize="s"
|
||||
>
|
||||
<div data-test-subj="transformEditAccordionRetentionPolicyContent">
|
||||
{' '}
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutRetentionPolicyFieldInput"
|
||||
errorMessages={formFields.retentionPolicyField.errorMessages}
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormRetentionPolicyFieldLabel',
|
||||
{
|
||||
defaultMessage: 'Field',
|
||||
}
|
||||
)}
|
||||
onChange={(value) => dispatch({ field: 'retentionPolicyField', value })}
|
||||
value={formFields.retentionPolicyField.value}
|
||||
/>
|
||||
{
|
||||
// If index pattern or date fields info not available
|
||||
// gracefully defaults to text input
|
||||
indexPatternId ? (
|
||||
<EuiFormRow
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormRetentionPolicyFieldLabel',
|
||||
{
|
||||
defaultMessage: 'Field',
|
||||
}
|
||||
)}
|
||||
isInvalid={formFields.retentionPolicyField.errorMessages.length > 0}
|
||||
error={formFields.retentionPolicyField.errorMessages}
|
||||
helpText={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormRetentionPolicyDateFieldHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'Select the date field that can be used to identify out of date documents in the destination index.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiSelect
|
||||
aria-label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormRetentionPolicyFieldSelectAriaLabel',
|
||||
{
|
||||
defaultMessage: 'Date field to set retention policy',
|
||||
}
|
||||
)}
|
||||
data-test-subj="transformEditFlyoutRetentionPolicyFieldSelect"
|
||||
options={retentionDateFieldOptions}
|
||||
value={formFields.retentionPolicyField.value}
|
||||
onChange={(e) =>
|
||||
dispatch({ field: 'retentionPolicyField', value: e.target.value })
|
||||
}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
) : (
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutRetentionPolicyFieldInput"
|
||||
errorMessages={formFields.retentionPolicyField.errorMessages}
|
||||
label={i18n.translate(
|
||||
'xpack.transform.transformList.editFlyoutFormRetentionPolicyFieldLabel',
|
||||
{
|
||||
defaultMessage: 'Field',
|
||||
}
|
||||
)}
|
||||
onChange={(value) => dispatch({ field: 'retentionPolicyField', value })}
|
||||
value={formFields.retentionPolicyField.value}
|
||||
/>
|
||||
)
|
||||
}
|
||||
<EditTransformFlyoutFormTextInput
|
||||
dataTestSubj="transformEditFlyoutRetentionPolicyMaxAgeInput"
|
||||
errorMessages={formFields.retentionPolicyMaxAge.errorMessages}
|
||||
|
|
|
@ -41,7 +41,11 @@ export const useActions = ({
|
|||
<>
|
||||
{startAction.isModalVisible && <StartActionModal {...startAction} />}
|
||||
{editAction.config && editAction.isFlyoutVisible && (
|
||||
<EditTransformFlyout closeFlyout={editAction.closeFlyout} config={editAction.config} />
|
||||
<EditTransformFlyout
|
||||
closeFlyout={editAction.closeFlyout}
|
||||
config={editAction.config}
|
||||
indexPatternId={editAction.indexPatternId}
|
||||
/>
|
||||
)}
|
||||
{deleteAction.isModalVisible && <DeleteActionModal {...deleteAction} />}
|
||||
</>
|
||||
|
|
|
@ -23566,7 +23566,6 @@
|
|||
"xpack.transform.clone.errorPromptText": "ソースインデックスパターンが存在するかどうかを確認するときにエラーが発生しました",
|
||||
"xpack.transform.clone.errorPromptTitle": "変換構成の取得中にエラーが発生しました。",
|
||||
"xpack.transform.clone.fetchErrorPromptText": "KibanaインデックスパターンIDを取得できませんでした。",
|
||||
"xpack.transform.clone.noIndexPatternErrorPromptText": "変換を複製できません。{indexPattern}のインデックスパターンが存在しません。",
|
||||
"xpack.transform.cloneTransform.breadcrumbTitle": "クローン変換",
|
||||
"xpack.transform.createTransform.breadcrumbTitle": "変換の作成",
|
||||
"xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage": "ディスティネーションインデックス{destinationIndex}の削除中にエラーが発生しました",
|
||||
|
|
|
@ -24117,7 +24117,6 @@
|
|||
"xpack.transform.clone.errorPromptText": "检查源索引模式是否存在时发生错误",
|
||||
"xpack.transform.clone.errorPromptTitle": "获取转换配置时发生错误。",
|
||||
"xpack.transform.clone.fetchErrorPromptText": "无法提取 Kibana 索引模式 ID。",
|
||||
"xpack.transform.clone.noIndexPatternErrorPromptText": "无法克隆转换。对于 {indexPattern},不存在索引模式。",
|
||||
"xpack.transform.cloneTransform.breadcrumbTitle": "克隆转换",
|
||||
"xpack.transform.createTransform.breadcrumbTitle": "创建转换",
|
||||
"xpack.transform.deleteTransform.deleteAnalyticsWithIndexErrorMessage": "删除目标索引 {destinationIndex} 时发生错误",
|
||||
|
|
|
@ -158,10 +158,7 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
'should have the retention policy inputs enabled'
|
||||
);
|
||||
await transform.editFlyout.openTransformEditAccordionRetentionPolicySettings();
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputEnabled(
|
||||
'RetentionPolicyField',
|
||||
true
|
||||
);
|
||||
await transform.editFlyout.assertTransformEditFlyoutRetentionPolicySelectEnabled(true);
|
||||
await transform.editFlyout.assertTransformEditFlyoutInputEnabled(
|
||||
'RetentionPolicyMaxAge',
|
||||
true
|
||||
|
|
|
@ -37,6 +37,21 @@ export function TransformEditFlyoutProvider({ getService }: FtrProviderContext)
|
|||
);
|
||||
},
|
||||
|
||||
async assertTransformEditFlyoutRetentionPolicySelectEnabled(expectedValue: boolean) {
|
||||
await testSubjects.existOrFail(`transformEditFlyoutRetentionPolicyFieldSelect`, {
|
||||
timeout: 1000,
|
||||
});
|
||||
const isEnabled = await testSubjects.isEnabled(
|
||||
`transformEditFlyoutRetentionPolicyFieldSelect`
|
||||
);
|
||||
expect(isEnabled).to.eql(
|
||||
expectedValue,
|
||||
`Expected 'transformEditFlyoutRetentionPolicyFieldSelect' input to be '${
|
||||
expectedValue ? 'enabled' : 'disabled'
|
||||
}' (got '${isEnabled ? 'enabled' : 'disabled'}')`
|
||||
);
|
||||
},
|
||||
|
||||
async assertTransformEditFlyoutInputEnabled(input: string, expectedValue: boolean) {
|
||||
await testSubjects.existOrFail(`transformEditFlyout${input}Input`, { timeout: 1000 });
|
||||
const isEnabled = await testSubjects.isEnabled(`transformEditFlyout${input}Input`);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue