mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[ML] Data Frame Analytics creation: improve existing job check (#89627)
* use jobsExist endpoint instead of preloaded job list * remove unused translation * memoize jobCheck so cancel call works correctly
This commit is contained in:
parent
4f6de5a407
commit
f53bc9825b
7 changed files with 85 additions and 41 deletions
|
@ -4,8 +4,8 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, Fragment, useEffect, useRef } from 'react';
|
||||
|
||||
import React, { FC, Fragment, useEffect, useMemo, useRef } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiCodeEditor,
|
||||
|
@ -22,6 +22,9 @@ import { XJsonMode } from '../../../../../../../shared_imports';
|
|||
|
||||
const xJsonMode = new XJsonMode();
|
||||
|
||||
import { useNotifications } from '../../../../../contexts/kibana';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
import { extractErrorMessage } from '../../../../../../../common/util/errors';
|
||||
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
||||
import { CreateStep } from '../create_step';
|
||||
import { ANALYTICS_STEPS } from '../../page';
|
||||
|
@ -42,11 +45,33 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = (prop
|
|||
} = state.form;
|
||||
|
||||
const forceInput = useRef<HTMLInputElement | null>(null);
|
||||
const { toasts } = useNotifications();
|
||||
|
||||
const onChange = (str: string) => {
|
||||
setAdvancedEditorRawString(str);
|
||||
};
|
||||
|
||||
const debouncedJobIdCheck = useMemo(
|
||||
() =>
|
||||
debounce(async () => {
|
||||
try {
|
||||
const { results } = await ml.dataFrameAnalytics.jobsExists([jobId], true);
|
||||
setFormState({ jobIdExists: results[jobId] });
|
||||
} catch (e) {
|
||||
toasts.addDanger(
|
||||
i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.advancedEditor.errorCheckingJobIdExists',
|
||||
{
|
||||
defaultMessage: 'The following error occurred checking if job id exists: {error}',
|
||||
values: { error: extractErrorMessage(e) },
|
||||
}
|
||||
)
|
||||
);
|
||||
}
|
||||
}, 400),
|
||||
[jobId]
|
||||
);
|
||||
|
||||
// Temp effect to close the context menu popover on Clone button click
|
||||
useEffect(() => {
|
||||
if (forceInput.current === null) {
|
||||
|
@ -57,6 +82,18 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = (prop
|
|||
forceInput.current.dispatchEvent(evt);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (jobIdValid === true) {
|
||||
debouncedJobIdCheck();
|
||||
} else if (typeof jobId === 'string' && jobId.trim() === '' && jobIdExists === true) {
|
||||
setFormState({ jobIdExists: false });
|
||||
}
|
||||
|
||||
return () => {
|
||||
debouncedJobIdCheck.cancel();
|
||||
};
|
||||
}, [jobId]);
|
||||
|
||||
return (
|
||||
<EuiForm className="mlDataFrameAnalyticsCreateForm">
|
||||
<EuiFormRow
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { FC, Fragment, useRef, useEffect, useState } from 'react';
|
||||
import React, { FC, Fragment, useRef, useEffect, useMemo, useState } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import {
|
||||
EuiFieldText,
|
||||
|
@ -94,6 +94,36 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
}
|
||||
}, 400);
|
||||
|
||||
const debouncedJobIdCheck = useMemo(
|
||||
() =>
|
||||
debounce(async () => {
|
||||
try {
|
||||
const { results } = await ml.dataFrameAnalytics.jobsExists([jobId], true);
|
||||
setFormState({ jobIdExists: results[jobId] });
|
||||
} catch (e) {
|
||||
notifications.toasts.addDanger(
|
||||
i18n.translate('xpack.ml.dataframe.analytics.create.errorCheckingJobIdExists', {
|
||||
defaultMessage: 'The following error occurred checking if job id exists: {error}',
|
||||
values: { error: extractErrorMessage(e) },
|
||||
})
|
||||
);
|
||||
}
|
||||
}, 400),
|
||||
[jobId]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (jobIdValid === true) {
|
||||
debouncedJobIdCheck();
|
||||
} else if (typeof jobId === 'string' && jobId.trim() === '' && jobIdExists === true) {
|
||||
setFormState({ jobIdExists: false });
|
||||
}
|
||||
|
||||
return () => {
|
||||
debouncedJobIdCheck.cancel();
|
||||
};
|
||||
}, [jobId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (destinationIndexNameValid === true) {
|
||||
debouncedIndexCheck();
|
||||
|
|
|
@ -499,7 +499,6 @@ export function reducer(state: State, action: Action): State {
|
|||
}
|
||||
|
||||
if (action.payload.jobId !== undefined) {
|
||||
newFormState.jobIdExists = state.jobIds.some((id) => newFormState.jobId === id);
|
||||
newFormState.jobIdEmpty = newFormState.jobId === '';
|
||||
newFormState.jobIdValid = isJobIdValid(newFormState.jobId);
|
||||
newFormState.jobIdInvalidMaxLength = !!maxLengthValidator(JOB_ID_MAX_LENGTH)(
|
||||
|
@ -542,12 +541,6 @@ export function reducer(state: State, action: Action): State {
|
|||
case ACTION.SET_JOB_CONFIG:
|
||||
return validateAdvancedEditor({ ...state, jobConfig: action.payload });
|
||||
|
||||
case ACTION.SET_JOB_IDS: {
|
||||
const newState = { ...state, jobIds: action.jobIds };
|
||||
newState.form.jobIdExists = newState.jobIds.some((id) => newState.form.jobId === id);
|
||||
return newState;
|
||||
}
|
||||
|
||||
case ACTION.SWITCH_TO_ADVANCED_EDITOR:
|
||||
const jobConfig = getJobConfigFromFormState(state.form);
|
||||
const shouldDisableSwitchToForm = isAdvancedConfig(jobConfig);
|
||||
|
@ -562,7 +555,7 @@ export function reducer(state: State, action: Action): State {
|
|||
});
|
||||
|
||||
case ACTION.SWITCH_TO_FORM:
|
||||
const { jobConfig: config, jobIds } = state;
|
||||
const { jobConfig: config } = state;
|
||||
const { jobId } = state.form;
|
||||
// @ts-ignore
|
||||
const formState = getFormStateFromJobConfig(config, false);
|
||||
|
@ -571,7 +564,6 @@ export function reducer(state: State, action: Action): State {
|
|||
formState.jobId = jobId;
|
||||
}
|
||||
|
||||
formState.jobIdExists = jobIds.some((id) => formState.jobId === id);
|
||||
formState.jobIdEmpty = jobId === '';
|
||||
formState.jobIdValid = isJobIdValid(jobId);
|
||||
formState.jobIdInvalidMaxLength = !!maxLengthValidator(JOB_ID_MAX_LENGTH)(jobId);
|
||||
|
|
|
@ -14,11 +14,7 @@ import { ml } from '../../../../../services/ml_api_service';
|
|||
import { useMlContext } from '../../../../../contexts/ml';
|
||||
import { DuplicateIndexPatternError } from '../../../../../../../../../../src/plugins/data/public';
|
||||
|
||||
import {
|
||||
useRefreshAnalyticsList,
|
||||
DataFrameAnalyticsId,
|
||||
DataFrameAnalyticsConfig,
|
||||
} from '../../../../common';
|
||||
import { useRefreshAnalyticsList, DataFrameAnalyticsConfig } from '../../../../common';
|
||||
import { extractCloningConfig, isAdvancedConfig } from '../../components/action_clone';
|
||||
|
||||
import { ActionDispatchers, ACTION } from './actions';
|
||||
|
@ -80,9 +76,6 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
dispatch({ type: ACTION.SET_IS_JOB_STARTED, isJobStarted });
|
||||
};
|
||||
|
||||
const setJobIds = (jobIds: DataFrameAnalyticsId[]) =>
|
||||
dispatch({ type: ACTION.SET_JOB_IDS, jobIds });
|
||||
|
||||
const resetRequestMessages = () => dispatch({ type: ACTION.RESET_REQUEST_MESSAGES });
|
||||
|
||||
const resetForm = () => dispatch({ type: ACTION.RESET_FORM });
|
||||
|
@ -180,25 +173,6 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
};
|
||||
|
||||
const prepareFormValidation = async () => {
|
||||
// re-fetch existing analytics job IDs and indices for form validation
|
||||
try {
|
||||
setJobIds(
|
||||
(await ml.dataFrameAnalytics.getDataFrameAnalytics()).data_frame_analytics.map(
|
||||
(job: DataFrameAnalyticsConfig) => job.id
|
||||
)
|
||||
);
|
||||
} catch (e) {
|
||||
addRequestMessage({
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList',
|
||||
{
|
||||
defaultMessage: 'An error occurred getting the existing data frame analytics job IDs:',
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
|
||||
try {
|
||||
// Set the existing index pattern titles.
|
||||
const indexPatternsMap: SourceIndexMap = {};
|
||||
|
|
|
@ -45,6 +45,11 @@ interface DeleteDataFrameAnalyticsWithIndexResponse {
|
|||
destIndexDeleted: DeleteDataFrameAnalyticsWithIndexStatus;
|
||||
destIndexPatternDeleted: DeleteDataFrameAnalyticsWithIndexStatus;
|
||||
}
|
||||
interface JobsExistsResponse {
|
||||
results: {
|
||||
[jobId: string]: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export const dataFrameAnalytics = {
|
||||
getDataFrameAnalytics(analyticsId?: string) {
|
||||
|
@ -98,6 +103,14 @@ export const dataFrameAnalytics = {
|
|||
query: { treatAsRoot, type },
|
||||
});
|
||||
},
|
||||
jobsExists(analyticsIds: string[], allSpaces: boolean = false) {
|
||||
const body = JSON.stringify({ analyticsIds, allSpaces });
|
||||
return http<JobsExistsResponse>({
|
||||
path: `${basePath()}/data_frame/analytics/jobs_exist`,
|
||||
method: 'POST',
|
||||
body,
|
||||
});
|
||||
},
|
||||
evaluateDataFrameAnalytics(evaluateConfig: any) {
|
||||
const body = JSON.stringify(evaluateConfig);
|
||||
return http<any>({
|
||||
|
|
|
@ -12595,7 +12595,6 @@
|
|||
"xpack.ml.dataframe.analytics.create.duplicateIndexPatternErrorMessageError": "インデックスパターン{indexPatternName}はすでに作成されています。",
|
||||
"xpack.ml.dataframe.analytics.create.errorCheckingIndexExists": "既存のインデックス名の取得中に次のエラーが発生しました:{error}",
|
||||
"xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob": "データフレーム分析ジョブの作成中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList": "既存のデータフレーム分析ジョブIDの取得中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles": "既存のインデックスパターンのタイトルの取得中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob": "データフレーム分析ジョブの開始中にエラーが発生しました。",
|
||||
"xpack.ml.dataframe.analytics.create.etaInputAriaLabel": "縮小が重みに適用されました",
|
||||
|
|
|
@ -12624,7 +12624,6 @@
|
|||
"xpack.ml.dataframe.analytics.create.duplicateIndexPatternErrorMessageError": "索引模式 {indexPatternName} 已存在。",
|
||||
"xpack.ml.dataframe.analytics.create.errorCheckingIndexExists": "获取现有索引名称时发生以下错误:{error}",
|
||||
"xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob": "创建数据帧分析作业时发生错误:",
|
||||
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList": "获取现有数据帧分析作业 ID 时发生错误:",
|
||||
"xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles": "获取现有索引模式标题时发生错误:",
|
||||
"xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob": "启动数据帧分析作业时发生错误:",
|
||||
"xpack.ml.dataframe.analytics.create.etaInputAriaLabel": "缩小量已应用于权重",
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue