[ML] DF Analytics creation: ensure monitor cluster privilege not required to create job (#71934)

* add checkIndexExists endpoint wrapping field_caps

* replace indexNames with checkIndexExists check

* update translations

* show error toast on index check fail

* add new route to api doc
This commit is contained in:
Melissa Alvarez 2020-07-16 13:22:25 -04:00 committed by GitHub
parent f75ccd4d8d
commit d0d271c07d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 87 additions and 38 deletions

View file

@ -4,7 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import React, { FC, Fragment, useRef } from 'react';
import React, { FC, Fragment, useRef, useEffect } from 'react';
import { debounce } from 'lodash';
import {
EuiFieldText,
EuiFormRow,
@ -21,6 +22,8 @@ import { CreateAnalyticsStepProps } from '../../../analytics_management/hooks/us
import { JOB_ID_MAX_LENGTH } from '../../../../../../../common/constants/validation';
import { ContinueButton } from '../continue_button';
import { ANALYTICS_STEPS } from '../../page';
import { ml } from '../../../../../services/ml_api_service';
import { extractErrorMessage } from '../../../../../../../common/util/errors';
export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
actions,
@ -28,7 +31,7 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
setCurrentStep,
}) => {
const {
services: { docLinks },
services: { docLinks, notifications },
} = useMlKibana();
const { ELASTIC_WEBSITE_URL, DOC_LINK_VERSION } = docLinks;
@ -59,6 +62,32 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
destinationIndexNameValid === false ||
(destinationIndexPatternTitleExists === true && createIndexPattern === true);
const debouncedIndexCheck = debounce(async () => {
try {
const { exists } = await ml.checkIndexExists({ index: destinationIndex });
setFormState({ destinationIndexNameExists: exists });
} catch (e) {
notifications.toasts.addDanger(
i18n.translate('xpack.ml.dataframe.analytics.create.errorCheckingIndexExists', {
defaultMessage: 'The following error occurred getting the existing index names: {error}',
values: { error: extractErrorMessage(e) },
})
);
}
}, 400);
useEffect(() => {
if (destinationIndexNameValid === true) {
debouncedIndexCheck();
} else if (destinationIndex.trim() === '' && destinationIndexNameExists === true) {
setFormState({ destinationIndexNameExists: false });
}
return () => {
debouncedIndexCheck.cancel();
};
}, [destinationIndex]);
return (
<Fragment>
<EuiFormRow

View file

@ -17,7 +17,6 @@ export enum ACTION {
RESET_FORM,
SET_ADVANCED_EDITOR_RAW_STRING,
SET_FORM_STATE,
SET_INDEX_NAMES,
SET_INDEX_PATTERN_TITLES,
SET_IS_JOB_CREATED,
SET_IS_JOB_STARTED,
@ -48,7 +47,6 @@ export type Action =
advancedEditorRawString: State['advancedEditorRawString'];
}
| { type: ACTION.SET_FORM_STATE; payload: Partial<State['form']> }
| { type: ACTION.SET_INDEX_NAMES; indexNames: State['indexNames'] }
| {
type: ACTION.SET_INDEX_PATTERN_TITLES;
payload: {

View file

@ -480,9 +480,6 @@ export function reducer(state: State, action: Action): State {
// update state attributes which are derived from other state attributes.
if (action.payload.destinationIndex !== undefined) {
newFormState.destinationIndexNameExists = state.indexNames.some(
(name) => newFormState.destinationIndex === name
);
newFormState.destinationIndexNameEmpty = newFormState.destinationIndex === '';
newFormState.destinationIndexNameValid = isValidIndexName(newFormState.destinationIndex);
newFormState.destinationIndexPatternTitleExists =
@ -514,14 +511,6 @@ export function reducer(state: State, action: Action): State {
? validateAdvancedEditor({ ...state, form: newFormState })
: validateForm({ ...state, form: newFormState });
case ACTION.SET_INDEX_NAMES: {
const newState = { ...state, indexNames: action.indexNames };
newState.form.destinationIndexNameExists = newState.indexNames.some(
(name) => newState.form.destinationIndex === name
);
return newState;
}
case ACTION.SET_INDEX_PATTERN_TITLES: {
const newState = {
...state,

View file

@ -94,7 +94,6 @@ export interface State {
trainingPercent: number;
};
disabled: boolean;
indexNames: EsIndexName[];
indexPatternsMap: SourceIndexMap;
isAdvancedEditorEnabled: boolean;
isAdvancedEditorValidJson: boolean;
@ -165,7 +164,6 @@ export const getInitialState = (): State => ({
!mlNodesAvailable() ||
!checkPermission('canCreateDataFrameAnalytics') ||
!checkPermission('canStartStopDataFrameAnalytics'),
indexNames: [],
indexPatternsMap: {},
isAdvancedEditorEnabled: false,
isAdvancedEditorValidJson: true,

View file

@ -25,7 +25,6 @@ import { reducer } from './reducer';
import {
getInitialState,
getJobConfigFromFormState,
EsIndexName,
FormMessage,
State,
SourceIndexMap,
@ -67,9 +66,6 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
const resetAdvancedEditorMessages = () =>
dispatch({ type: ACTION.RESET_ADVANCED_EDITOR_MESSAGES });
const setIndexNames = (indexNames: EsIndexName[]) =>
dispatch({ type: ACTION.SET_INDEX_NAMES, indexNames });
const setAdvancedEditorRawString = (advancedEditorRawString: string) =>
dispatch({ type: ACTION.SET_ADVANCED_EDITOR_RAW_STRING, advancedEditorRawString });
@ -214,21 +210,7 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
}
try {
setIndexNames((await ml.getIndices()).map((index) => index.name));
} catch (e) {
addRequestMessage({
error: getErrorMessage(e),
message: i18n.translate(
'xpack.ml.dataframe.analytics.create.errorGettingDataFrameIndexNames',
{
defaultMessage: 'An error occurred getting the existing index names:',
}
),
});
}
try {
// Set the index pattern titles which the user can choose as the source.
// Set the existing index pattern titles.
const indexPatternsMap: SourceIndexMap = {};
const savedObjects = (await mlContext.indexPatterns.getCache()) || [];
savedObjects.forEach((obj) => {

View file

@ -372,6 +372,16 @@ export function mlApiServicesProvider(httpService: HttpService) {
});
},
checkIndexExists({ index }: { index: string }) {
const body = JSON.stringify({ index });
return httpService.http<{ exists: boolean }>({
path: `${basePath()}/index_exists`,
method: 'POST',
body,
});
},
getFieldCaps({ index, fields }: { index: string; fields: string[] }) {
const body = JSON.stringify({
...(index !== undefined ? { index } : {}),

View file

@ -105,6 +105,7 @@
"MlNodeCount",
"MlInfo",
"MlEsSearch",
"MlIndexExists",
"JobAuditMessages",
"GetJobAuditMessages",

View file

@ -226,4 +226,48 @@ export function systemRoutes(
}
})
);
/**
* @apiGroup SystemRoutes
*
* @api {post} /api/ml/index_exists ES Field caps wrapper checks if index exists
* @apiName MlIndexExists
*/
router.post(
{
path: '/api/ml/index_exists',
validate: {
body: schema.object({ index: schema.string() }),
},
options: {
tags: ['access:ml:canGetJobs'],
},
},
mlLicense.fullLicenseAPIGuard(async (context, request, response) => {
try {
const { index } = request.body;
const options = {
index: [index],
fields: ['*'],
ignoreUnavailable: true,
allowNoIndices: true,
ignore: 404,
};
const fieldsResult = await context.ml!.mlClient.callAsCurrentUser('fieldCaps', options);
const result = { exists: false };
if (Array.isArray(fieldsResult.indices) && fieldsResult.indices.length !== 0) {
result.exists = true;
}
return response.ok({
body: result,
});
} catch (error) {
return response.customError(wrapError(error));
}
})
);
}

View file

@ -9483,7 +9483,6 @@
"xpack.ml.dataframe.analytics.create.duplicateIndexPatternErrorMessageError": "インデックスパターン{indexPatternName}はすでに作成されています。",
"xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob": "データフレーム分析ジョブの作成中にエラーが発生しました。",
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList": "既存のデータフレーム分析ジョブIDの取得中にエラーが発生しました。",
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameIndexNames": "既存のインデックス名の取得中にエラーが発生しました。",
"xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles": "既存のインデックスパターンのタイトルの取得中にエラーが発生しました。",
"xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob": "データフレーム分析ジョブの開始中にエラーが発生しました。",
"xpack.ml.dataframe.analytics.create.indexPatternAlreadyExistsError": "このタイトルのインデックスパターンが既に存在します。",

View file

@ -9488,7 +9488,6 @@
"xpack.ml.dataframe.analytics.create.duplicateIndexPatternErrorMessageError": "索引模式 {indexPatternName} 已存在。",
"xpack.ml.dataframe.analytics.create.errorCreatingDataFrameAnalyticsJob": "创建数据帧分析作业时发生错误:",
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameAnalyticsList": "获取现有数据帧分析作业 ID 时发生错误:",
"xpack.ml.dataframe.analytics.create.errorGettingDataFrameIndexNames": "获取现有索引名称时发生错误:",
"xpack.ml.dataframe.analytics.create.errorGettingIndexPatternTitles": "获取现有索引模式标题时发生错误:",
"xpack.ml.dataframe.analytics.create.errorStartingDataFrameAnalyticsJob": "启动数据帧分析作业时发生错误:",
"xpack.ml.dataframe.analytics.create.indexPatternAlreadyExistsError": "具有此名称的索引模式已存在。",