mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[ML] Data Frames: Improve start/stop behavior (#36757)
- Adds a confirm modal to the Start button of the data frame jobs list. image - Adds a snippet with a warning about possibly cluster load to wizard. image - Uses _stop?force=true to stop jobs. - Adds a check to disable the start button in the jobs list if the job is a completed batch job.
This commit is contained in:
parent
6ab8d133b7
commit
1e25d1d5e5
14 changed files with 382 additions and 176 deletions
|
@ -211,16 +211,19 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
return `PUT _data_frame/transforms/${jobId}\n${JSON.stringify(jobConfig, null, 2)}\n\n`;
|
||||
}
|
||||
|
||||
const ITEM_STYLE = { width: '300px' };
|
||||
// TODO move this to SASS
|
||||
const FLEX_GROUP_STYLE = { height: '90px', maxWidth: '800px' };
|
||||
const FLEX_ITEM_STYLE = { width: '200px' };
|
||||
const PANEL_ITEM_STYLE = { width: '300px' };
|
||||
|
||||
return (
|
||||
<EuiForm>
|
||||
{!created && (
|
||||
<EuiFlexGroup alignItems="center" style={{ maxWidth: '800px' }}>
|
||||
<EuiFlexItem grow={false} style={{ width: '200px' }}>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton fill isDisabled={created && started} onClick={createAndStartDataFrame}>
|
||||
{i18n.translate('xpack.ml.dataframe.jobCreateForm.createAndStartDataFrameButton', {
|
||||
defaultMessage: 'Create & start',
|
||||
defaultMessage: 'Create and start',
|
||||
})}
|
||||
</EuiButton>
|
||||
</EuiFlexItem>
|
||||
|
@ -230,7 +233,7 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
'xpack.ml.dataframe.jobCreateForm.createAndStartDataFrameDescription',
|
||||
{
|
||||
defaultMessage:
|
||||
'Create and start the data frame job. After the job is started, you will be offered options to continue exploring the data frame job.',
|
||||
'Creates and starts the data frame job. A data frame job will increase search and indexing load in your cluster. Please stop the job if excessive load is experienced. After the job is started, you will be offered options to continue exploring the data frame job.',
|
||||
}
|
||||
)}
|
||||
</EuiText>
|
||||
|
@ -238,9 +241,9 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
</EuiFlexGroup>
|
||||
)}
|
||||
{created && (
|
||||
<EuiFlexGroup alignItems="center" style={{ maxWidth: '800px' }}>
|
||||
<EuiFlexItem grow={false} style={{ width: '200px' }}>
|
||||
<EuiButton isDisabled={created && started} onClick={startDataFrame}>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton fill isDisabled={created && started} onClick={startDataFrame}>
|
||||
{i18n.translate('xpack.ml.dataframe.jobCreateForm.startDataFrameButton', {
|
||||
defaultMessage: 'Start',
|
||||
})}
|
||||
|
@ -250,14 +253,14 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
<EuiText color="subdued" size="s">
|
||||
{i18n.translate('xpack.ml.dataframe.jobCreateForm.startDataFrameDescription', {
|
||||
defaultMessage:
|
||||
'Starts the data frame job. After the job is started, you will be offered options to continue exploring the data frame job.',
|
||||
'Starts the data frame job. A data frame job will increase search and indexing load in your cluster. Please stop the job if excessive load is experienced. After the job is started, you will be offered options to continue exploring the data frame job.',
|
||||
})}
|
||||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
)}
|
||||
<EuiFlexGroup alignItems="center" style={{ maxWidth: '800px' }}>
|
||||
<EuiFlexItem grow={false} style={{ width: '200px' }}>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiButton isDisabled={created} onClick={createDataFrame}>
|
||||
{i18n.translate('xpack.ml.dataframe.jobCreateForm.createDataFrameButton', {
|
||||
defaultMessage: 'Create',
|
||||
|
@ -273,8 +276,8 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
</EuiText>
|
||||
</EuiFlexItem>
|
||||
</EuiFlexGroup>
|
||||
<EuiFlexGroup alignItems="center" style={{ maxWidth: '800px' }}>
|
||||
<EuiFlexItem grow={false} style={{ width: '200px' }}>
|
||||
<EuiFlexGroup alignItems="center" style={FLEX_GROUP_STYLE}>
|
||||
<EuiFlexItem grow={false} style={FLEX_ITEM_STYLE}>
|
||||
<EuiCopy textToCopy={getJobConfigDevConsoleStatement()}>
|
||||
{(copy: () => void) => (
|
||||
<EuiButton onClick={copy} style={{ width: '100%' }}>
|
||||
|
@ -324,7 +327,7 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
<Fragment>
|
||||
<EuiHorizontalRule />
|
||||
<EuiFlexGrid gutterSize="l">
|
||||
<EuiFlexItem style={ITEM_STYLE}>
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="list" />}
|
||||
title={i18n.translate('xpack.ml.dataframe.jobCreateForm.jobsListCardTitle', {
|
||||
|
@ -340,7 +343,7 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
/>
|
||||
</EuiFlexItem>
|
||||
{started === true && createIndexPattern === true && indexPatternId === undefined && (
|
||||
<EuiFlexItem style={ITEM_STYLE}>
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiPanel style={{ position: 'relative' }}>
|
||||
<EuiProgress size="xs" color="primary" position="absolute" />
|
||||
<EuiText color="subdued" size="s">
|
||||
|
@ -357,7 +360,7 @@ export const JobCreateForm: SFC<Props> = React.memo(
|
|||
</EuiFlexItem>
|
||||
)}
|
||||
{started === true && indexPatternId !== undefined && (
|
||||
<EuiFlexItem style={ITEM_STYLE}>
|
||||
<EuiFlexItem style={PANEL_ITEM_STYLE}>
|
||||
<EuiCard
|
||||
icon={<EuiIcon size="xxl" type="discoverApp" />}
|
||||
title={i18n.translate('xpack.ml.dataframe.jobCreateForm.discoverCardTitle', {
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`Data Frame: Job List Actions <StartAction /> Minimal initialization 1`] = `
|
||||
<Fragment>
|
||||
<EuiToolTip
|
||||
content="Your license has expired. Please contact your administrator."
|
||||
delay="regular"
|
||||
position="top"
|
||||
>
|
||||
<EuiButtonEmpty
|
||||
aria-label="Start"
|
||||
color="text"
|
||||
disabled={true}
|
||||
iconSide="left"
|
||||
iconType="play"
|
||||
onClick={[Function]}
|
||||
size="xs"
|
||||
type="button"
|
||||
>
|
||||
Start
|
||||
</EuiButtonEmpty>
|
||||
</EuiToolTip>
|
||||
</Fragment>
|
||||
`;
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { DataFrameJobListRow } from './common';
|
||||
import { DeleteAction } from './action_delete';
|
||||
|
||||
import dataFrameJobListRow from './__mocks__/data_frame_job_list_row.json';
|
||||
|
||||
describe('Data Frame: Job List Actions <DeleteAction />', () => {
|
||||
test('Minimal initialization', () => {
|
||||
const item: DataFrameJobListRow = dataFrameJobListRow;
|
||||
const props = {
|
||||
disabled: false,
|
||||
item,
|
||||
deleteJob(d: DataFrameJobListRow) {},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<DeleteAction {...props} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,114 @@
|
|||
/*
|
||||
* 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, { Fragment, SFC, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiConfirmModal,
|
||||
EuiOverlayMask,
|
||||
EuiToolTip,
|
||||
EUI_MODAL_CONFIRM_BUTTON,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import {
|
||||
checkPermission,
|
||||
createPermissionFailureMessage,
|
||||
} from '../../../../../privilege/check_privilege';
|
||||
|
||||
import { DataFrameJobListRow, DATA_FRAME_RUNNING_STATE } from './common';
|
||||
|
||||
interface DeleteActionProps {
|
||||
item: DataFrameJobListRow;
|
||||
deleteJob(d: DataFrameJobListRow): void;
|
||||
}
|
||||
|
||||
export const DeleteAction: SFC<DeleteActionProps> = ({ deleteJob, item }) => {
|
||||
const disabled = item.state.task_state === DATA_FRAME_RUNNING_STATE.STARTED;
|
||||
|
||||
const canDeleteDataFrameJob: boolean = checkPermission('canDeleteDataFrameJob');
|
||||
|
||||
const [isModalVisible, setModalVisible] = useState(false);
|
||||
|
||||
const closeModal = () => setModalVisible(false);
|
||||
const deleteAndCloseModal = () => {
|
||||
setModalVisible(false);
|
||||
deleteJob(item);
|
||||
};
|
||||
const openModal = () => setModalVisible(true);
|
||||
|
||||
const buttonDeleteText = i18n.translate('xpack.ml.dataframe.jobsList.deleteActionName', {
|
||||
defaultMessage: 'Delete',
|
||||
});
|
||||
|
||||
let deleteButton = (
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
color="text"
|
||||
disabled={disabled || !canDeleteDataFrameJob}
|
||||
iconType="trash"
|
||||
onClick={openModal}
|
||||
aria-label={buttonDeleteText}
|
||||
>
|
||||
{buttonDeleteText}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
if (disabled || !canDeleteDataFrameJob) {
|
||||
deleteButton = (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
disabled
|
||||
? i18n.translate('xpack.ml.dataframe.jobsList.deleteActionDisabledToolTipContent', {
|
||||
defaultMessage: 'Stop the data frame job in order to delete it.',
|
||||
})
|
||||
: createPermissionFailureMessage('canStartStopDataFrameJob')
|
||||
}
|
||||
>
|
||||
{deleteButton}
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{deleteButton}
|
||||
{isModalVisible && (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.dataframe.jobsList.deleteModalTitle', {
|
||||
defaultMessage: 'Delete {jobId}',
|
||||
values: { jobId: item.config.id },
|
||||
})}
|
||||
onCancel={closeModal}
|
||||
onConfirm={deleteAndCloseModal}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.ml.dataframe.jobsList.deleteModalCancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.ml.dataframe.jobsList.deleteModalDeleteButton',
|
||||
{
|
||||
defaultMessage: 'Delete',
|
||||
}
|
||||
)}
|
||||
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
|
||||
buttonColor="danger"
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.ml.dataframe.jobsList.deleteModalBody', {
|
||||
defaultMessage: 'Are you sure you want to delete this job?',
|
||||
})}
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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 { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { DataFrameJobListRow } from './common';
|
||||
import { StartAction } from './action_start';
|
||||
|
||||
import dataFrameJobListRow from './__mocks__/data_frame_job_list_row.json';
|
||||
|
||||
describe('Data Frame: Job List Actions <StartAction />', () => {
|
||||
test('Minimal initialization', () => {
|
||||
const item: DataFrameJobListRow = dataFrameJobListRow;
|
||||
const props = {
|
||||
disabled: false,
|
||||
item,
|
||||
startJob(d: DataFrameJobListRow) {},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<StartAction {...props} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
|
@ -0,0 +1,111 @@
|
|||
/*
|
||||
* 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, { Fragment, SFC, useState } from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiConfirmModal,
|
||||
EuiOverlayMask,
|
||||
EuiToolTip,
|
||||
EUI_MODAL_CONFIRM_BUTTON,
|
||||
} from '@elastic/eui';
|
||||
|
||||
import {
|
||||
checkPermission,
|
||||
createPermissionFailureMessage,
|
||||
} from '../../../../../privilege/check_privilege';
|
||||
|
||||
import { DataFrameJobListRow, isCompletedBatchJob } from './common';
|
||||
|
||||
interface StartActionProps {
|
||||
item: DataFrameJobListRow;
|
||||
startJob(d: DataFrameJobListRow): void;
|
||||
}
|
||||
|
||||
export const StartAction: SFC<StartActionProps> = ({ startJob, item }) => {
|
||||
const canStartStopDataFrameJob: boolean = checkPermission('canStartStopDataFrameJob');
|
||||
|
||||
const [isModalVisible, setModalVisible] = useState(false);
|
||||
|
||||
const closeModal = () => setModalVisible(false);
|
||||
const startAndCloseModal = () => {
|
||||
setModalVisible(false);
|
||||
startJob(item);
|
||||
};
|
||||
const openModal = () => setModalVisible(true);
|
||||
|
||||
const buttonStartText = i18n.translate('xpack.ml.dataframe.jobsList.startActionName', {
|
||||
defaultMessage: 'Start',
|
||||
});
|
||||
|
||||
// Disable start for batch jobs which have completed.
|
||||
const completedBatchJob = isCompletedBatchJob(item);
|
||||
|
||||
let startButton = (
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
color="text"
|
||||
disabled={!canStartStopDataFrameJob || completedBatchJob}
|
||||
iconType="play"
|
||||
onClick={openModal}
|
||||
aria-label={buttonStartText}
|
||||
>
|
||||
{buttonStartText}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
if (!canStartStopDataFrameJob || completedBatchJob) {
|
||||
startButton = (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
!canStartStopDataFrameJob
|
||||
? createPermissionFailureMessage('canStartStopDataFrameJob')
|
||||
: i18n.translate('xpack.ml.dataframe.jobsList.completeBatchJobToolTip', {
|
||||
defaultMessage: '{jobId} is a completed batch job and cannot be restarted.',
|
||||
values: { jobId: item.config.id },
|
||||
})
|
||||
}
|
||||
>
|
||||
{startButton}
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{startButton}
|
||||
{isModalVisible && (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.dataframe.jobsList.startModalTitle', {
|
||||
defaultMessage: 'Start {jobId}',
|
||||
values: { jobId: item.config.id },
|
||||
})}
|
||||
onCancel={closeModal}
|
||||
onConfirm={startAndCloseModal}
|
||||
cancelButtonText={i18n.translate('xpack.ml.dataframe.jobsList.startModalCancelButton', {
|
||||
defaultMessage: 'Cancel',
|
||||
})}
|
||||
confirmButtonText={i18n.translate('xpack.ml.dataframe.jobsList.startModalStartButton', {
|
||||
defaultMessage: 'Start',
|
||||
})}
|
||||
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
|
||||
buttonColor="primary"
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.ml.dataframe.jobsList.startModalBody', {
|
||||
defaultMessage:
|
||||
'A data frame job will increase search and indexing load in your cluster. Please stop the job if excessive load is experienced. Are you sure you want to start this job?',
|
||||
})}
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -4,28 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { shallow } from 'enzyme';
|
||||
import React from 'react';
|
||||
|
||||
import { DataFrameJobListRow } from './common';
|
||||
import { DeleteAction, getActions } from './actions';
|
||||
|
||||
import dataFrameJobListRow from './__mocks__/data_frame_job_list_row.json';
|
||||
|
||||
describe('Data Frame: Job List Actions <DeleteAction />', () => {
|
||||
test('Minimal initialization', () => {
|
||||
const item: DataFrameJobListRow = dataFrameJobListRow;
|
||||
const props = {
|
||||
disabled: false,
|
||||
item,
|
||||
deleteJob(d: DataFrameJobListRow) {},
|
||||
};
|
||||
|
||||
const wrapper = shallow(<DeleteAction {...props} />);
|
||||
|
||||
expect(wrapper).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
import { getActions } from './actions';
|
||||
|
||||
describe('Data Frame: Job List Actions', () => {
|
||||
test('getActions()', () => {
|
||||
|
|
|
@ -4,15 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { Fragment, SFC, useState } from 'react';
|
||||
import React from 'react';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import {
|
||||
EuiButtonEmpty,
|
||||
EuiConfirmModal,
|
||||
EuiOverlayMask,
|
||||
EuiToolTip,
|
||||
EUI_MODAL_CONFIRM_BUTTON,
|
||||
} from '@elastic/eui';
|
||||
import { EuiButtonEmpty, EuiToolTip } from '@elastic/eui';
|
||||
|
||||
import {
|
||||
checkPermission,
|
||||
|
@ -22,96 +16,8 @@ import {
|
|||
import { DataFrameJobListRow, DATA_FRAME_RUNNING_STATE } from './common';
|
||||
import { deleteJobFactory, startJobFactory, stopJobFactory } from './job_service';
|
||||
|
||||
interface DeleteActionProps {
|
||||
disabled: boolean;
|
||||
item: DataFrameJobListRow;
|
||||
deleteJob(d: DataFrameJobListRow): void;
|
||||
}
|
||||
|
||||
export const DeleteAction: SFC<DeleteActionProps> = ({ deleteJob, disabled, item }) => {
|
||||
const canDeleteDataFrameJob: boolean = checkPermission('canDeleteDataFrameJob');
|
||||
|
||||
const [isModalVisible, setModalVisible] = useState(false);
|
||||
|
||||
const closeModal = () => setModalVisible(false);
|
||||
const deleteAndCloseModal = () => {
|
||||
setModalVisible(false);
|
||||
deleteJob(item);
|
||||
};
|
||||
const openModal = () => setModalVisible(true);
|
||||
|
||||
const buttonDeleteText = i18n.translate('xpack.ml.dataframe.jobsList.deleteActionName', {
|
||||
defaultMessage: 'Delete',
|
||||
});
|
||||
|
||||
let deleteButton = (
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
color="text"
|
||||
disabled={disabled || !canDeleteDataFrameJob}
|
||||
iconType="trash"
|
||||
onClick={openModal}
|
||||
aria-label={buttonDeleteText}
|
||||
>
|
||||
{buttonDeleteText}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
if (disabled || !canDeleteDataFrameJob) {
|
||||
deleteButton = (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={
|
||||
disabled
|
||||
? i18n.translate('xpack.ml.dataframe.jobsList.deleteActionDisabledToolTipContent', {
|
||||
defaultMessage: 'Stop the data frame job in order to delete it.',
|
||||
})
|
||||
: createPermissionFailureMessage('canStartStopDataFrameJob')
|
||||
}
|
||||
>
|
||||
{deleteButton}
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
{deleteButton}
|
||||
{isModalVisible && (
|
||||
<EuiOverlayMask>
|
||||
<EuiConfirmModal
|
||||
title={i18n.translate('xpack.ml.dataframe.jobsList.deleteModalTitle', {
|
||||
defaultMessage: 'Delete {jobId}',
|
||||
values: { jobId: item.config.id },
|
||||
})}
|
||||
onCancel={closeModal}
|
||||
onConfirm={deleteAndCloseModal}
|
||||
cancelButtonText={i18n.translate(
|
||||
'xpack.ml.dataframe.jobsList.deleteModalCancelButton',
|
||||
{
|
||||
defaultMessage: 'Cancel',
|
||||
}
|
||||
)}
|
||||
confirmButtonText={i18n.translate(
|
||||
'xpack.ml.dataframe.jobsList.deleteModalDeleteButton',
|
||||
{
|
||||
defaultMessage: 'Delete',
|
||||
}
|
||||
)}
|
||||
defaultFocusedButton={EUI_MODAL_CONFIRM_BUTTON}
|
||||
buttonColor="danger"
|
||||
>
|
||||
<p>
|
||||
{i18n.translate('xpack.ml.dataframe.jobsList.deleteModalBody', {
|
||||
defaultMessage: 'Are you sure you want to delete this job?',
|
||||
})}
|
||||
</p>
|
||||
</EuiConfirmModal>
|
||||
</EuiOverlayMask>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
||||
import { StartAction } from './action_start';
|
||||
import { DeleteAction } from './action_delete';
|
||||
|
||||
export const getActions = (getJobs: () => void) => {
|
||||
const canStartStopDataFrameJob: boolean = checkPermission('canStartStopDataFrameJob');
|
||||
|
@ -125,35 +31,7 @@ export const getActions = (getJobs: () => void) => {
|
|||
isPrimary: true,
|
||||
render: (item: DataFrameJobListRow) => {
|
||||
if (item.state.task_state !== DATA_FRAME_RUNNING_STATE.STARTED) {
|
||||
const buttonStartText = i18n.translate('xpack.ml.dataframe.jobsList.startActionName', {
|
||||
defaultMessage: 'Start',
|
||||
});
|
||||
|
||||
const startButton = (
|
||||
<EuiButtonEmpty
|
||||
size="xs"
|
||||
color="text"
|
||||
disabled={!canStartStopDataFrameJob}
|
||||
iconType="play"
|
||||
onClick={() => startJob(item)}
|
||||
aria-label={buttonStartText}
|
||||
>
|
||||
{buttonStartText}
|
||||
</EuiButtonEmpty>
|
||||
);
|
||||
|
||||
if (!canStartStopDataFrameJob) {
|
||||
return (
|
||||
<EuiToolTip
|
||||
position="top"
|
||||
content={createPermissionFailureMessage('canStartStopDataFrameJob')}
|
||||
>
|
||||
{startButton}
|
||||
</EuiToolTip>
|
||||
);
|
||||
}
|
||||
|
||||
return startButton;
|
||||
return <StartAction startJob={startJob} item={item} />;
|
||||
}
|
||||
|
||||
const buttonStopText = i18n.translate('xpack.ml.dataframe.jobsList.stopActionName', {
|
||||
|
@ -188,13 +66,7 @@ export const getActions = (getJobs: () => void) => {
|
|||
},
|
||||
{
|
||||
render: (item: DataFrameJobListRow) => {
|
||||
return (
|
||||
<DeleteAction
|
||||
deleteJob={deleteJob}
|
||||
disabled={item.state.task_state === DATA_FRAME_RUNNING_STATE.STARTED}
|
||||
item={item}
|
||||
/>
|
||||
);
|
||||
return <DeleteAction deleteJob={deleteJob} item={item} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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 mockDataFrameJobListRow from './__mocks__/data_frame_job_list_row.json';
|
||||
|
||||
import { DATA_FRAME_RUNNING_STATE, isCompletedBatchJob } from './common';
|
||||
|
||||
describe('Data Frame: isCompletedBatchJob()', () => {
|
||||
test('isCompletedBatchJob()', () => {
|
||||
// check the job config/state against the conditions
|
||||
// that will be used by isCompletedBatchJob()
|
||||
// followed by a call to isCompletedBatchJob() itself
|
||||
expect(mockDataFrameJobListRow.state.checkpoint === 1).toBe(true);
|
||||
expect(mockDataFrameJobListRow.sync === undefined).toBe(true);
|
||||
expect(mockDataFrameJobListRow.state.task_state === DATA_FRAME_RUNNING_STATE.STOPPED).toBe(
|
||||
true
|
||||
);
|
||||
expect(isCompletedBatchJob(mockDataFrameJobListRow)).toBe(true);
|
||||
|
||||
// adapt the mock config to resemble a non-completed job.
|
||||
mockDataFrameJobListRow.state.checkpoint = 0;
|
||||
expect(isCompletedBatchJob(mockDataFrameJobListRow)).toBe(false);
|
||||
});
|
||||
});
|
|
@ -12,6 +12,7 @@ export interface DataFrameJob {
|
|||
dest: string;
|
||||
id: JobId;
|
||||
source: string;
|
||||
sync?: object;
|
||||
}
|
||||
|
||||
export enum DATA_FRAME_RUNNING_STATE {
|
||||
|
@ -63,3 +64,13 @@ export enum DataFrameJobListColumn {
|
|||
}
|
||||
|
||||
export type ItemIdToExpandedRowMap = Dictionary<JSX.Element>;
|
||||
|
||||
export function isCompletedBatchJob(item: DataFrameJobListRow) {
|
||||
// If `checkpoint=1`, `sync` is missing from the config and state is stopped,
|
||||
// then this is a completed batch data frame job.
|
||||
return (
|
||||
item.state.checkpoint === 1 &&
|
||||
item.config.sync === undefined &&
|
||||
item.state.task_state === DATA_FRAME_RUNNING_STATE.STOPPED
|
||||
);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ export const dataFrame = {
|
|||
},
|
||||
stopDataFrameTransformsJob(jobId) {
|
||||
return http({
|
||||
url: `${basePath}/_data_frame/transforms/${jobId}/_stop`,
|
||||
url: `${basePath}/_data_frame/transforms/${jobId}/_stop?force=true`,
|
||||
method: 'POST',
|
||||
});
|
||||
},
|
||||
|
|
|
@ -187,10 +187,13 @@ export const elasticsearchJsPlugin = (Client, config, components) => {
|
|||
ml.stopDataFrameTransformsJob = ca({
|
||||
urls: [
|
||||
{
|
||||
fmt: '/_data_frame/transforms/<%=jobId%>/_stop',
|
||||
fmt: '/_data_frame/transforms/<%=jobId%>/_stop?&force=<%=force%>',
|
||||
req: {
|
||||
jobId: {
|
||||
type: 'string'
|
||||
},
|
||||
force: {
|
||||
type: 'boolean'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -109,8 +109,14 @@ export function dataFrameRoutes(server, commonRouteConfig) {
|
|||
path: '/api/ml/_data_frame/transforms/{jobId}/_stop',
|
||||
handler(request) {
|
||||
const callWithRequest = callWithRequestFactory(server, request);
|
||||
const { jobId } = request.params;
|
||||
return callWithRequest('ml.stopDataFrameTransformsJob', { jobId })
|
||||
const options = {
|
||||
jobId: request.params.jobId
|
||||
};
|
||||
const force = request.query.force;
|
||||
if (force !== undefined) {
|
||||
options.force = force;
|
||||
}
|
||||
return callWithRequest('ml.stopDataFrameTransformsJob', options)
|
||||
.catch(resp => wrapError(resp));
|
||||
},
|
||||
config: {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue