mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Data frame analytics wizard: Only allow data view creation if job will be started (#120042)
* Only allow data view creation if job will be started * update tests * no stat fetching when job not started * ensure result index exists before dataview creation attempt * update error message and increase retry delay * use getIdsWithTitle to avoid fieldsWithWildcard call * move dataview validation to create step * use exists api in ml index_exists endpoint * refactor retry function * fix duplicate i18n ids Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
bd886e85da
commit
3da312a127
12 changed files with 290 additions and 242 deletions
|
@ -7,19 +7,10 @@
|
|||
|
||||
import React, { FC, Fragment, useEffect, useMemo, useRef } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import {
|
||||
EuiCallOut,
|
||||
EuiFieldText,
|
||||
EuiForm,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiSwitch,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { EuiCallOut, EuiFieldText, EuiForm, EuiFormRow, EuiSpacer } from '@elastic/eui';
|
||||
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { CodeEditor } from '../../../../../../../../../../src/plugins/kibana_react/public';
|
||||
import { useNotifications } from '../../../../../contexts/kibana';
|
||||
import { ml } from '../../../../../services/ml_api_service';
|
||||
|
@ -34,33 +25,15 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = (prop
|
|||
|
||||
const { advancedEditorMessages, advancedEditorRawString, isJobCreated } = state;
|
||||
|
||||
const {
|
||||
createIndexPattern,
|
||||
destinationIndexPatternTitleExists,
|
||||
jobId,
|
||||
jobIdEmpty,
|
||||
jobIdExists,
|
||||
jobIdValid,
|
||||
} = state.form;
|
||||
const { jobId, jobIdEmpty, jobIdExists, jobIdValid } = state.form;
|
||||
|
||||
const forceInput = useRef<HTMLInputElement | null>(null);
|
||||
const { toasts } = useNotifications();
|
||||
const {
|
||||
services: {
|
||||
application: { capabilities },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const onChange = (str: string) => {
|
||||
setAdvancedEditorRawString(str);
|
||||
};
|
||||
|
||||
const canCreateDataView = useMemo(
|
||||
() =>
|
||||
capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true,
|
||||
[capabilities]
|
||||
);
|
||||
|
||||
const debouncedJobIdCheck = useMemo(
|
||||
() =>
|
||||
debounce(async () => {
|
||||
|
@ -217,47 +190,6 @@ export const CreateAnalyticsAdvancedEditor: FC<CreateAnalyticsFormProps> = (prop
|
|||
<EuiSpacer />
|
||||
</Fragment>
|
||||
))}
|
||||
{!isJobCreated && (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
isInvalid={
|
||||
(createIndexPattern && destinationIndexPatternTitleExists) ||
|
||||
canCreateDataView === false
|
||||
}
|
||||
error={[
|
||||
...(canCreateDataView === false
|
||||
? [
|
||||
<EuiText size="xs" color="warning">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.dataViewPermissionWarning',
|
||||
{
|
||||
defaultMessage: 'You need permission to create data views.',
|
||||
}
|
||||
)}
|
||||
</EuiText>,
|
||||
]
|
||||
: []),
|
||||
...(createIndexPattern && destinationIndexPatternTitleExists
|
||||
? [
|
||||
i18n.translate('xpack.ml.dataframe.analytics.create.dataViewExistsError', {
|
||||
defaultMessage: 'A data view with this title already exists.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<EuiSwitch
|
||||
disabled={canCreateDataView === false}
|
||||
name="mlDataFrameAnalyticsCreateIndexPattern"
|
||||
label={i18n.translate('xpack.ml.dataframe.analytics.create.createDataViewLabel', {
|
||||
defaultMessage: 'Create data view',
|
||||
})}
|
||||
checked={createIndexPattern === true}
|
||||
onChange={() => setFormState({ createIndexPattern: !createIndexPattern })}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</Fragment>
|
||||
)}
|
||||
<EuiSpacer />
|
||||
<CreateStep {...props} step={ANALYTICS_STEPS.CREATE} />
|
||||
</EuiForm>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 2.0.
|
||||
*/
|
||||
|
||||
import React, { FC, useState } from 'react';
|
||||
import React, { FC, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
EuiButton,
|
||||
EuiCheckbox,
|
||||
|
@ -13,9 +13,11 @@ import {
|
|||
EuiFlexItem,
|
||||
EuiFormRow,
|
||||
EuiSpacer,
|
||||
EuiText,
|
||||
} from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
import { CreateAnalyticsFormProps } from '../../../analytics_management/hooks/use_create_analytics_form';
|
||||
import { Messages } from '../shared';
|
||||
import { ANALYTICS_STEPS } from '../../page';
|
||||
|
@ -26,14 +28,38 @@ interface Props extends CreateAnalyticsFormProps {
|
|||
}
|
||||
|
||||
export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
||||
const { createAnalyticsJob, startAnalyticsJob } = actions;
|
||||
const { isAdvancedEditorValidJson, isJobCreated, isJobStarted, isValid, requestMessages } = state;
|
||||
const { jobId, jobType } = state.form;
|
||||
const {
|
||||
services: {
|
||||
application: { capabilities },
|
||||
},
|
||||
} = useMlKibana();
|
||||
|
||||
const [checked, setChecked] = useState<boolean>(true);
|
||||
const canCreateDataView = useMemo(
|
||||
() =>
|
||||
capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true,
|
||||
[capabilities]
|
||||
);
|
||||
|
||||
const { createAnalyticsJob, setFormState, startAnalyticsJob } = actions;
|
||||
const { isAdvancedEditorValidJson, isJobCreated, isJobStarted, isValid, requestMessages } = state;
|
||||
const {
|
||||
createIndexPattern,
|
||||
destinationIndex,
|
||||
destinationIndexPatternTitleExists,
|
||||
jobId,
|
||||
jobType,
|
||||
} = state.form;
|
||||
|
||||
const [startChecked, setStartChecked] = useState<boolean>(true);
|
||||
const [creationTriggered, setCreationTriggered] = useState<boolean>(false);
|
||||
const [showProgress, setShowProgress] = useState<boolean>(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (canCreateDataView === false) {
|
||||
setFormState({ createIndexPattern: false });
|
||||
}
|
||||
}, [capabilities]);
|
||||
|
||||
if (step !== ANALYTICS_STEPS.CREATE) return null;
|
||||
|
||||
const handleCreation = async () => {
|
||||
|
@ -44,7 +70,7 @@ export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
|||
setCreationTriggered(false);
|
||||
}
|
||||
|
||||
if (checked && creationSuccess === true) {
|
||||
if (startChecked && creationSuccess === true) {
|
||||
setShowProgress(true);
|
||||
startAnalyticsJob();
|
||||
}
|
||||
|
@ -53,32 +79,114 @@ export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
|||
return (
|
||||
<div data-test-subj="mlAnalyticsCreateJobWizardCreateStep active">
|
||||
{!isJobCreated && !isJobStarted && (
|
||||
<EuiFlexGroup gutterSize="s">
|
||||
<EuiFlexGroup gutterSize="m" alignItems="center">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
helpText={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.startCheckboxHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'If unselected, job can be started later by returning to the jobs list.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiCheckbox
|
||||
data-test-subj="mlAnalyticsCreateJobWizardStartJobCheckbox"
|
||||
id={'dataframe-create-start-checkbox'}
|
||||
label={i18n.translate('xpack.ml.dataframe.analytics.create.wizardStartCheckbox', {
|
||||
defaultMessage: 'Start immediately',
|
||||
})}
|
||||
checked={checked}
|
||||
onChange={(e) => setChecked(e.target.checked)}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiFlexGroup direction="column" gutterSize="s">
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
helpText={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.startCheckboxHelpText',
|
||||
{
|
||||
defaultMessage:
|
||||
'If unselected, job can be started later by returning to the jobs list.',
|
||||
}
|
||||
)}
|
||||
>
|
||||
<EuiCheckbox
|
||||
data-test-subj="mlAnalyticsCreateJobWizardStartJobCheckbox"
|
||||
id={'dataframe-create-start-checkbox'}
|
||||
label={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.wizardStartCheckbox',
|
||||
{
|
||||
defaultMessage: 'Start immediately',
|
||||
}
|
||||
)}
|
||||
checked={startChecked}
|
||||
onChange={(e) => {
|
||||
setStartChecked(e.target.checked);
|
||||
if (e.target.checked === false) {
|
||||
setFormState({ createIndexPattern: false });
|
||||
}
|
||||
}}
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
{startChecked ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
isInvalid={
|
||||
(createIndexPattern && destinationIndexPatternTitleExists) ||
|
||||
createIndexPattern === false ||
|
||||
canCreateDataView === false
|
||||
}
|
||||
error={[
|
||||
...(canCreateDataView === false
|
||||
? [
|
||||
<EuiText size="xs" color="warning">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.dataViewPermissionWarning',
|
||||
{
|
||||
defaultMessage: 'You need permission to create data views.',
|
||||
}
|
||||
)}
|
||||
</EuiText>,
|
||||
]
|
||||
: []),
|
||||
...(createIndexPattern && destinationIndexPatternTitleExists
|
||||
? [
|
||||
i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.dataViewExistsError',
|
||||
{
|
||||
defaultMessage:
|
||||
'A data view with the title {title} already exists.',
|
||||
values: { title: destinationIndex },
|
||||
}
|
||||
),
|
||||
]
|
||||
: []),
|
||||
...(!createIndexPattern && !destinationIndexPatternTitleExists
|
||||
? [
|
||||
<EuiText size="xs" color="warning">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'You may not be able to view job results if a data view is not created for the destination index.',
|
||||
}
|
||||
)}
|
||||
</EuiText>,
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<EuiCheckbox
|
||||
disabled={isJobCreated || canCreateDataView === false}
|
||||
name="mlDataFrameAnalyticsCreateIndexPattern"
|
||||
id={'dataframe-create-data-view-checkbox'}
|
||||
label={i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createDataViewLabel',
|
||||
{
|
||||
defaultMessage: 'Create data view',
|
||||
}
|
||||
)}
|
||||
checked={createIndexPattern === true}
|
||||
onChange={() => setFormState({ createIndexPattern: !createIndexPattern })}
|
||||
data-test-subj="mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
</EuiFlexItem>
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
<EuiFlexItem grow={false}>
|
||||
<EuiButton
|
||||
className="mlAnalyticsCreateWizard__footerButton"
|
||||
disabled={!isValid || !isAdvancedEditorValidJson}
|
||||
disabled={
|
||||
!isValid ||
|
||||
!isAdvancedEditorValidJson ||
|
||||
(destinationIndexPatternTitleExists === true && createIndexPattern === true)
|
||||
}
|
||||
onClick={handleCreation}
|
||||
fill
|
||||
isLoading={creationTriggered}
|
||||
|
@ -93,9 +201,9 @@ export const CreateStep: FC<Props> = ({ actions, state, step }) => {
|
|||
)}
|
||||
<EuiSpacer size="s" />
|
||||
<Messages messages={requestMessages} />
|
||||
{isJobCreated === true && (
|
||||
{isJobCreated === true ? (
|
||||
<CreateStepFooter jobId={jobId} jobType={jobType!} showProgress={showProgress} />
|
||||
)}
|
||||
) : null}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -54,6 +54,8 @@ export const CreateStepFooter: FC<Props> = ({ jobId, jobType, showProgress }) =>
|
|||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (showProgress === false) return;
|
||||
|
||||
const interval = setInterval(async () => {
|
||||
try {
|
||||
const analyticsStats = await ml.dataFrameAnalytics.getDataFrameAnalyticsStats(jobId);
|
||||
|
@ -129,11 +131,11 @@ export const CreateStepFooter: FC<Props> = ({ jobId, jobType, showProgress }) =>
|
|||
<EuiFlexItem grow={false}>
|
||||
<BackToListPanel />
|
||||
</EuiFlexItem>
|
||||
{jobFinished === true && (
|
||||
{jobFinished === true ? (
|
||||
<EuiFlexItem grow={false}>
|
||||
<ViewResultsPanel jobId={jobId} analysisType={jobType} />
|
||||
</EuiFlexItem>
|
||||
)}
|
||||
) : null}
|
||||
</EuiFlexGroup>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
|
|
|
@ -7,15 +7,7 @@
|
|||
|
||||
import React, { FC, Fragment, useRef, useEffect, useMemo, useState } from 'react';
|
||||
import { debounce } from 'lodash';
|
||||
import {
|
||||
EuiFieldText,
|
||||
EuiFormRow,
|
||||
EuiLink,
|
||||
EuiSpacer,
|
||||
EuiSwitch,
|
||||
EuiText,
|
||||
EuiTextArea,
|
||||
} from '@elastic/eui';
|
||||
import { EuiFieldText, EuiFormRow, EuiLink, EuiSpacer, EuiSwitch, EuiTextArea } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { useMlKibana } from '../../../../../contexts/kibana';
|
||||
|
@ -42,23 +34,17 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
setCurrentStep,
|
||||
}) => {
|
||||
const {
|
||||
services: {
|
||||
docLinks,
|
||||
notifications,
|
||||
application: { capabilities },
|
||||
},
|
||||
services: { docLinks, notifications },
|
||||
} = useMlKibana();
|
||||
const createIndexLink = docLinks.links.apis.createIndex;
|
||||
const { setFormState } = actions;
|
||||
const { form, cloneJob, hasSwitchedToEditor, isJobCreated } = state;
|
||||
const {
|
||||
createIndexPattern,
|
||||
description,
|
||||
destinationIndex,
|
||||
destinationIndexNameEmpty,
|
||||
destinationIndexNameExists,
|
||||
destinationIndexNameValid,
|
||||
destinationIndexPatternTitleExists,
|
||||
jobId,
|
||||
jobIdEmpty,
|
||||
jobIdExists,
|
||||
|
@ -75,11 +61,6 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
(cloneJob !== undefined && resultsField === DEFAULT_RESULTS_FIELD)
|
||||
);
|
||||
|
||||
const canCreateDataView = useMemo(
|
||||
() =>
|
||||
capabilities.savedObjectsManagement.edit === true || capabilities.indexPatterns.save === true,
|
||||
[capabilities]
|
||||
);
|
||||
const forceInput = useRef<HTMLInputElement | null>(null);
|
||||
|
||||
const isStepInvalid =
|
||||
|
@ -87,8 +68,7 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
jobIdExists === true ||
|
||||
jobIdValid === false ||
|
||||
destinationIndexNameEmpty === true ||
|
||||
destinationIndexNameValid === false ||
|
||||
(destinationIndexPatternTitleExists === true && createIndexPattern === true);
|
||||
destinationIndexNameValid === false;
|
||||
|
||||
const debouncedIndexCheck = debounce(async () => {
|
||||
try {
|
||||
|
@ -158,12 +138,6 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
}
|
||||
}, [destIndexSameAsId, jobId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (canCreateDataView === false) {
|
||||
setFormState({ createIndexPattern: false });
|
||||
}
|
||||
}, [capabilities]);
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
<EuiFormRow
|
||||
|
@ -359,56 +333,6 @@ export const DetailsStepForm: FC<CreateAnalyticsStepProps> = ({
|
|||
/>
|
||||
</EuiFormRow>
|
||||
)}
|
||||
<EuiFormRow
|
||||
fullWidth
|
||||
isInvalid={
|
||||
(createIndexPattern && destinationIndexPatternTitleExists) ||
|
||||
createIndexPattern === false ||
|
||||
canCreateDataView === false
|
||||
}
|
||||
error={[
|
||||
...(canCreateDataView === false
|
||||
? [
|
||||
<EuiText size="xs" color="warning">
|
||||
{i18n.translate('xpack.ml.dataframe.analytics.create.dataViewPermissionWarning', {
|
||||
defaultMessage: 'You need permission to create data views.',
|
||||
})}
|
||||
</EuiText>,
|
||||
]
|
||||
: []),
|
||||
...(createIndexPattern && destinationIndexPatternTitleExists
|
||||
? [
|
||||
i18n.translate('xpack.ml.dataframe.analytics.create.dataViewExistsError', {
|
||||
defaultMessage: 'A data view with this title already exists.',
|
||||
}),
|
||||
]
|
||||
: []),
|
||||
...(!createIndexPattern
|
||||
? [
|
||||
<EuiText size="xs" color="warning">
|
||||
{i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.shouldCreateDataViewMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'You may not be able to view job results if a data view is not created for the destination index.',
|
||||
}
|
||||
)}
|
||||
</EuiText>,
|
||||
]
|
||||
: []),
|
||||
]}
|
||||
>
|
||||
<EuiSwitch
|
||||
disabled={isJobCreated === true || canCreateDataView === false}
|
||||
name="mlDataFrameAnalyticsCreateIndexPattern"
|
||||
label={i18n.translate('xpack.ml.dataframe.analytics.create.createDataViewLabel', {
|
||||
defaultMessage: 'Create data view',
|
||||
})}
|
||||
checked={createIndexPattern === true}
|
||||
onChange={() => setFormState({ createIndexPattern: !createIndexPattern })}
|
||||
data-test-subj="mlAnalyticsCreateJobWizardCreateIndexPatternSwitch"
|
||||
/>
|
||||
</EuiFormRow>
|
||||
<EuiSpacer />
|
||||
<ContinueButton
|
||||
isDisabled={isStepInvalid}
|
||||
|
|
|
@ -57,7 +57,7 @@ export const useDeleteAction = (canDeleteDataFrameAnalytics: boolean) => {
|
|||
|
||||
const checkIndexPatternExists = async () => {
|
||||
try {
|
||||
const dv = (await dataViews.find(indexName)).find(({ title }) => title === indexName);
|
||||
const dv = (await dataViews.getIdsWithTitle()).find(({ title }) => title === indexName);
|
||||
if (dv !== undefined) {
|
||||
setIndexPatternExists(true);
|
||||
} else {
|
||||
|
|
|
@ -47,6 +47,49 @@ export interface CreateAnalyticsStepProps extends CreateAnalyticsFormProps {
|
|||
stepActivated?: boolean;
|
||||
}
|
||||
|
||||
async function checkIndexExists(destinationIndex: string) {
|
||||
let resp;
|
||||
let errorMessage;
|
||||
try {
|
||||
resp = await ml.checkIndicesExists({ indices: [destinationIndex] });
|
||||
} catch (e) {
|
||||
errorMessage = extractErrorMessage(e);
|
||||
}
|
||||
return { resp, errorMessage };
|
||||
}
|
||||
|
||||
async function retryIndexExistsCheck(
|
||||
destinationIndex: string
|
||||
): Promise<{ success: boolean; indexExists: boolean; errorMessage?: string }> {
|
||||
let retryCount = 15;
|
||||
|
||||
let resp = await checkIndexExists(destinationIndex);
|
||||
let indexExists = resp.resp && resp.resp[destinationIndex] && resp.resp[destinationIndex].exists;
|
||||
|
||||
while (retryCount > 1 && !indexExists) {
|
||||
retryCount--;
|
||||
await delay(1000);
|
||||
resp = await checkIndexExists(destinationIndex);
|
||||
indexExists = resp.resp && resp.resp[destinationIndex] && resp.resp[destinationIndex].exists;
|
||||
}
|
||||
|
||||
if (indexExists) {
|
||||
return { success: true, indexExists: true };
|
||||
}
|
||||
|
||||
return {
|
||||
success: false,
|
||||
indexExists: false,
|
||||
...(resp.errorMessage !== undefined ? { errorMessage: resp.errorMessage } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function delay(ms = 1000) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
}
|
||||
|
||||
export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
||||
const mlContext = useMlContext();
|
||||
const [state, dispatch] = useReducer(reducer, getInitialState());
|
||||
|
@ -125,49 +168,88 @@ export const useCreateAnalyticsForm = (): CreateAnalyticsFormProps => {
|
|||
|
||||
const createKibanaIndexPattern = async () => {
|
||||
const dataViewName = destinationIndex;
|
||||
|
||||
try {
|
||||
await mlContext.dataViewsContract.createAndSave(
|
||||
{
|
||||
title: dataViewName,
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
|
||||
addRequestMessage({
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createDataViewSuccessMessage',
|
||||
{
|
||||
defaultMessage: 'Kibana data view {dataViewName} created.',
|
||||
values: { dataViewName },
|
||||
const exists = await retryIndexExistsCheck(destinationIndex);
|
||||
if (exists?.success === true) {
|
||||
// index exists - create data view
|
||||
if (exists?.indexExists === true) {
|
||||
try {
|
||||
await mlContext.dataViewsContract.createAndSave(
|
||||
{
|
||||
title: dataViewName,
|
||||
},
|
||||
false,
|
||||
true
|
||||
);
|
||||
addRequestMessage({
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createDataViewSuccessMessage',
|
||||
{
|
||||
defaultMessage: 'Kibana data view {dataViewName} created.',
|
||||
values: { dataViewName },
|
||||
}
|
||||
),
|
||||
});
|
||||
} catch (e) {
|
||||
// handle data view creation error
|
||||
if (e instanceof DuplicateDataViewError) {
|
||||
addRequestMessage({
|
||||
error: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessageError',
|
||||
{
|
||||
defaultMessage: 'The data view {dataViewName} already exists.',
|
||||
values: { dataViewName },
|
||||
}
|
||||
),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessage',
|
||||
{
|
||||
defaultMessage: 'An error occurred creating the Kibana data view:',
|
||||
}
|
||||
),
|
||||
});
|
||||
} else {
|
||||
addRequestMessage({
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createDataViewErrorMessage',
|
||||
{
|
||||
defaultMessage: 'An error occurred creating the Kibana data view:',
|
||||
}
|
||||
),
|
||||
});
|
||||
}
|
||||
),
|
||||
});
|
||||
} catch (e) {
|
||||
if (e instanceof DuplicateDataViewError) {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Ran out of retries or there was a problem checking index exists
|
||||
if (exists?.errorMessage) {
|
||||
addRequestMessage({
|
||||
error: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessageError',
|
||||
'xpack.ml.dataframe.analytics.create.errorCheckingDestinationIndexDataFrameAnalyticsJob',
|
||||
{
|
||||
defaultMessage: 'The data view {dataViewName} already exists.',
|
||||
values: { dataViewName },
|
||||
defaultMessage: '{errorMessage}',
|
||||
values: { errorMessage: exists.errorMessage },
|
||||
}
|
||||
),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.duplicateDataViewErrorMessage',
|
||||
'xpack.ml.dataframe.analytics.create.errorOccurredCheckingDestinationIndexDataFrameAnalyticsJob',
|
||||
{
|
||||
defaultMessage: 'An error occurred creating the Kibana data view:',
|
||||
defaultMessage: 'An error occurred checking destination index exists.',
|
||||
}
|
||||
),
|
||||
});
|
||||
} else {
|
||||
addRequestMessage({
|
||||
error: extractErrorMessage(e),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.createDataViewErrorMessage',
|
||||
error: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.destinationIndexNotCreatedForDataFrameAnalyticsJob',
|
||||
{
|
||||
defaultMessage: 'An error occurred creating the Kibana data view:',
|
||||
defaultMessage: 'Destination index has not yet been created.',
|
||||
}
|
||||
),
|
||||
message: i18n.translate(
|
||||
'xpack.ml.dataframe.analytics.create.unableToCreateDataViewForDataFrameAnalyticsJob',
|
||||
{
|
||||
defaultMessage: 'Unable to create data view.',
|
||||
}
|
||||
),
|
||||
});
|
||||
|
|
|
@ -118,6 +118,7 @@
|
|||
"MlInfo",
|
||||
"MlEsSearch",
|
||||
"MlIndexExists",
|
||||
"MlSpecificIndexExists",
|
||||
|
||||
"JobAuditMessages",
|
||||
"GetJobAuditMessages",
|
||||
|
|
|
@ -225,17 +225,16 @@ export function systemRoutes(
|
|||
try {
|
||||
const { indices } = request.body;
|
||||
|
||||
const options = {
|
||||
index: indices,
|
||||
fields: ['*'],
|
||||
ignore_unavailable: true,
|
||||
allow_no_indices: true,
|
||||
};
|
||||
const results = await Promise.all(
|
||||
indices.map(async (index) =>
|
||||
client.asCurrentUser.indices.exists({
|
||||
index,
|
||||
})
|
||||
)
|
||||
);
|
||||
|
||||
const { body } = await client.asCurrentUser.fieldCaps(options);
|
||||
|
||||
const result = indices.reduce((acc, cur) => {
|
||||
acc[cur] = { exists: body.indices.includes(cur) };
|
||||
const result = indices.reduce((acc, cur, i) => {
|
||||
acc[cur] = { exists: results[i].body };
|
||||
return acc;
|
||||
}, {} as Record<string, { exists: boolean }>);
|
||||
|
||||
|
|
|
@ -195,12 +195,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
|
||||
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('continues to the validation step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToValidationStep();
|
||||
|
||||
|
@ -215,6 +209,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
await ml.testExecution.logTestStep('continues to the create step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
});
|
||||
|
||||
it('runs the analytics job and displays it correctly in the job list', async () => {
|
||||
|
|
|
@ -217,12 +217,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
|
||||
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('continues to the validation step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToValidationStep();
|
||||
|
||||
|
@ -232,6 +226,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
await ml.testExecution.logTestStep('continues to the create step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
});
|
||||
|
||||
it('runs the analytics job and displays it correctly in the job list', async () => {
|
||||
|
|
|
@ -189,12 +189,6 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
await ml.dataFrameAnalyticsCreation.assertDestIndexInputExists();
|
||||
await ml.dataFrameAnalyticsCreation.setDestIndex(testData.destinationIndex);
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
|
||||
await ml.testExecution.logTestStep('continues to the validation step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToValidationStep();
|
||||
|
||||
|
@ -204,6 +198,12 @@ export default function ({ getService }: FtrProviderContext) {
|
|||
|
||||
await ml.testExecution.logTestStep('continues to the create step');
|
||||
await ml.dataFrameAnalyticsCreation.continueToCreateStep();
|
||||
|
||||
await ml.testExecution.logTestStep('sets the create data view switch');
|
||||
await ml.dataFrameAnalyticsCreation.assertCreateIndexPatternSwitchExists();
|
||||
await ml.dataFrameAnalyticsCreation.setCreateIndexPatternSwitchState(
|
||||
testData.createIndexPattern
|
||||
);
|
||||
});
|
||||
|
||||
it('runs the analytics job and displays it correctly in the job list', async () => {
|
||||
|
|
|
@ -532,15 +532,15 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
|
|||
},
|
||||
|
||||
async assertCreateIndexPatternSwitchExists() {
|
||||
await testSubjects.existOrFail(`mlAnalyticsCreateJobWizardCreateIndexPatternSwitch`, {
|
||||
await testSubjects.existOrFail(`mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox`, {
|
||||
allowHidden: true,
|
||||
});
|
||||
},
|
||||
|
||||
async getCreateIndexPatternSwitchCheckState(): Promise<boolean> {
|
||||
const state = await testSubjects.getAttribute(
|
||||
'mlAnalyticsCreateJobWizardCreateIndexPatternSwitch',
|
||||
'aria-checked'
|
||||
'mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox',
|
||||
'checked'
|
||||
);
|
||||
return state === 'true';
|
||||
},
|
||||
|
@ -584,7 +584,7 @@ export function MachineLearningDataFrameAnalyticsCreationProvider(
|
|||
|
||||
async setCreateIndexPatternSwitchState(checkState: boolean) {
|
||||
if ((await this.getCreateIndexPatternSwitchCheckState()) !== checkState) {
|
||||
await testSubjects.click('mlAnalyticsCreateJobWizardCreateIndexPatternSwitch');
|
||||
await testSubjects.click('mlAnalyticsCreateJobWizardCreateIndexPatternCheckbox');
|
||||
}
|
||||
await this.assertCreateIndexPatternSwitchCheckState(checkState);
|
||||
},
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue