mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 09:48:58 -04:00
[ML] Adding post create job options (#43205)
* [ML] Adding post create job options * adding toasts * fixing toast string * tweaking continue job function * updating ids * removing ts-ignore
This commit is contained in:
parent
6da05e0b8d
commit
4acfc04f85
11 changed files with 175 additions and 14 deletions
|
@ -80,8 +80,12 @@ class CreateWatchFlyoutUI extends Component {
|
|||
}
|
||||
}
|
||||
|
||||
closeFlyout = () => {
|
||||
this.setState({ isFlyoutVisible: false });
|
||||
closeFlyout = (watchCreated = false) => {
|
||||
this.setState({ isFlyoutVisible: false }, ()=>{
|
||||
if (typeof this.props.flyoutHidden === 'function') {
|
||||
this.props.flyoutHidden(watchCreated);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
showFlyout = (jobId) => {
|
||||
|
@ -107,7 +111,7 @@ class CreateWatchFlyoutUI extends Component {
|
|||
mlCreateWatchService.createNewWatch(this.state.jobId)
|
||||
.then((resp) => {
|
||||
toastNotifications.addSuccess(getSuccessToast(resp.id, resp.url, intl));
|
||||
this.closeFlyout();
|
||||
this.closeFlyout(true);
|
||||
})
|
||||
.catch((error) => {
|
||||
toastNotifications.addDanger(intl.formatMessage({
|
||||
|
@ -194,6 +198,7 @@ class CreateWatchFlyoutUI extends Component {
|
|||
CreateWatchFlyoutUI.propTypes = {
|
||||
setShowFunction: PropTypes.func.isRequired,
|
||||
unsetShowFunction: PropTypes.func.isRequired,
|
||||
flyoutHidden: PropTypes.func,
|
||||
};
|
||||
|
||||
export const CreateWatchFlyout = injectI18n(CreateWatchFlyoutUI);
|
||||
|
|
|
@ -430,7 +430,6 @@ export class JobsListView extends Component {
|
|||
<CreateWatchFlyout
|
||||
setShowFunction={this.setShowCreateWatchFlyoutFunction}
|
||||
unsetShowFunction={this.unsetShowCreateWatchFlyoutFunction}
|
||||
compile={this.props.compile}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -247,11 +247,12 @@ export class JobCreator {
|
|||
return this._subscribers;
|
||||
}
|
||||
|
||||
public async createAndStartJob() {
|
||||
public async createAndStartJob(): Promise<JobRunner> {
|
||||
try {
|
||||
await this.createJob();
|
||||
await this.createDatafeed();
|
||||
await this.startDatafeed();
|
||||
const jobRunner = await this.startDatafeed();
|
||||
return jobRunner;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
|
|
|
@ -141,6 +141,7 @@ export class MultiMetricJobCreator extends JobCreator {
|
|||
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
|
||||
this._overrideConfigs(job, datafeed);
|
||||
this.jobId = '';
|
||||
this.createdBy = CREATED_BY_LABEL.MULTI_METRIC;
|
||||
const detectors = getRichDetectors(job.analysis_config.detectors);
|
||||
|
||||
this.removeAllDetectors();
|
||||
|
|
|
@ -130,6 +130,7 @@ export class PopulationJobCreator extends JobCreator {
|
|||
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
|
||||
this._overrideConfigs(job, datafeed);
|
||||
this.jobId = '';
|
||||
this.createdBy = CREATED_BY_LABEL.POPULATION;
|
||||
const detectors = getRichDetectors(job.analysis_config.detectors);
|
||||
|
||||
this.removeAllDetectors();
|
||||
|
|
|
@ -181,6 +181,7 @@ export class SingleMetricJobCreator extends JobCreator {
|
|||
public cloneFromExistingJob(job: Job, datafeed: Datafeed) {
|
||||
this._overrideConfigs(job, datafeed);
|
||||
this.jobId = '';
|
||||
this.createdBy = CREATED_BY_LABEL.SINGLE_METRIC;
|
||||
const detectors = getRichDetectors(job.analysis_config.detectors);
|
||||
|
||||
this.removeAllDetectors();
|
||||
|
|
|
@ -66,10 +66,20 @@ export class JobRunner {
|
|||
// start the datafeed and then start polling for progress
|
||||
// the complete percentage is added to an observable
|
||||
// so all pre-subscribed listeners can follow along.
|
||||
public async startDatafeed(): Promise<void> {
|
||||
private async _startDatafeed(
|
||||
start: number | undefined,
|
||||
end: number | undefined,
|
||||
pollProgress: boolean
|
||||
): Promise<boolean> {
|
||||
try {
|
||||
await this.openJob();
|
||||
await mlJobService.startDatafeed(this._datafeedId, this._jobId, this._start, this._end);
|
||||
const { started } = await mlJobService.startDatafeed(
|
||||
this._datafeedId,
|
||||
this._jobId,
|
||||
start,
|
||||
end
|
||||
);
|
||||
|
||||
this._datafeedState = DATAFEED_STATE.STARTED;
|
||||
this._percentageComplete = 0;
|
||||
|
||||
|
@ -87,12 +97,25 @@ export class JobRunner {
|
|||
};
|
||||
// wait for the first check to run and then return success.
|
||||
// all subsequent checks will update the observable
|
||||
await check();
|
||||
if (pollProgress === true) {
|
||||
await check();
|
||||
}
|
||||
return started;
|
||||
} catch (error) {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async startDatafeed() {
|
||||
return await this._startDatafeed(this._start, this._end, true);
|
||||
}
|
||||
|
||||
public async startDatafeedInRealTime(continueJob: boolean) {
|
||||
// if continuing a job, set the start to be the end date
|
||||
const start = continueJob ? this._end : this._start;
|
||||
return await this._startDatafeed(start, undefined, false);
|
||||
}
|
||||
|
||||
public async getProgress(): Promise<{ progress: Progress; isRunning: boolean }> {
|
||||
return await ml.jobs.getLookBackProgress(this._jobId, this._start, this._end);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
export { PostSaveOptions } from './post_save_options';
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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, { FC, Fragment, useContext, useState } from 'react';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { EuiButton } from '@elastic/eui';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import { JobRunner } from '../../../../../common/job_runner';
|
||||
|
||||
// @ts-ignore
|
||||
import { CreateWatchFlyout } from '../../../../../../jobs_list/components/create_watch_flyout';
|
||||
import { JobCreatorContext } from '../../../../components/job_creator_context';
|
||||
import { DATAFEED_STATE } from '../../../../../../../../common/constants/states';
|
||||
|
||||
interface Props {
|
||||
jobRunner: JobRunner | null;
|
||||
}
|
||||
|
||||
type ShowFlyout = (jobId: string) => void;
|
||||
|
||||
export const PostSaveOptions: FC<Props> = ({ jobRunner }) => {
|
||||
const { jobCreator } = useContext(JobCreatorContext);
|
||||
const [datafeedState, setDatafeedState] = useState(DATAFEED_STATE.STOPPED);
|
||||
const [watchFlyoutVisible, setWatchFlyoutVisible] = useState(false);
|
||||
const [watchCreated, setWatchCreated] = useState(false);
|
||||
|
||||
function setShowCreateWatchFlyoutFunction(showFlyout: ShowFlyout) {
|
||||
showFlyout(jobCreator.jobId);
|
||||
}
|
||||
|
||||
function flyoutHidden(jobCreated: boolean) {
|
||||
setWatchFlyoutVisible(false);
|
||||
setWatchCreated(jobCreated);
|
||||
}
|
||||
|
||||
function unsetShowCreateWatchFlyoutFunction() {
|
||||
setWatchFlyoutVisible(false);
|
||||
}
|
||||
|
||||
async function startJobInRealTime() {
|
||||
setDatafeedState(DATAFEED_STATE.STARTING);
|
||||
if (jobRunner !== null) {
|
||||
try {
|
||||
const started = await jobRunner.startDatafeedInRealTime(true);
|
||||
setDatafeedState(started === true ? DATAFEED_STATE.STARTED : DATAFEED_STATE.STOPPED);
|
||||
toastNotifications.addSuccess({
|
||||
title: i18n.translate('xpack.ml.newJob.wizard.startJobInRealTimeSuccess', {
|
||||
defaultMessage: `Job {jobId} started`,
|
||||
values: { jobId: jobCreator.jobId },
|
||||
}),
|
||||
});
|
||||
} catch (error) {
|
||||
setDatafeedState(DATAFEED_STATE.STOPPED);
|
||||
toastNotifications.addDanger({
|
||||
title: i18n.translate('xpack.ml.newJob.wizard.startJobInRealTimeError', {
|
||||
defaultMessage: `Error starting job`,
|
||||
}),
|
||||
text: error.message,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Fragment>
|
||||
 
|
||||
<EuiButton
|
||||
isDisabled={
|
||||
datafeedState === DATAFEED_STATE.STARTING || datafeedState === DATAFEED_STATE.STARTED
|
||||
}
|
||||
onClick={startJobInRealTime}
|
||||
data-test-subj="mlButtonUseFullData3"
|
||||
>
|
||||
<FormattedMessage
|
||||
id="xpack.ml.newJob.wizard.startJobInRealTime"
|
||||
defaultMessage="Start job running in real time"
|
||||
/>
|
||||
</EuiButton>
|
||||
 
|
||||
<EuiButton
|
||||
isDisabled={
|
||||
datafeedState === DATAFEED_STATE.STOPPED ||
|
||||
datafeedState === DATAFEED_STATE.STARTING ||
|
||||
watchCreated === true
|
||||
}
|
||||
onClick={() => setWatchFlyoutVisible(true)}
|
||||
data-test-subj="mlButtonUseFullData"
|
||||
>
|
||||
<FormattedMessage id="xpack.ml.newJob.wizard.createWatch" defaultMessage="Create watch" />
|
||||
</EuiButton>
|
||||
{datafeedState === DATAFEED_STATE.STARTED && watchFlyoutVisible && (
|
||||
<CreateWatchFlyout
|
||||
setShowFunction={setShowCreateWatchFlyoutFunction}
|
||||
unsetShowFunction={unsetShowCreateWatchFlyoutFunction}
|
||||
flyoutHidden={flyoutHidden}
|
||||
/>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
};
|
|
@ -11,12 +11,14 @@ import { toastNotifications } from 'ui/notify';
|
|||
import { WizardNav } from '../wizard_nav';
|
||||
import { WIZARD_STEPS, StepProps } from '../step_types';
|
||||
import { JobCreatorContext } from '../job_creator_context';
|
||||
import { JobRunner } from '../../../common/job_runner';
|
||||
import { mlJobService } from '../../../../../services/job_service';
|
||||
import { JsonFlyout } from './json_flyout';
|
||||
import { isSingleMetricJobCreator } from '../../../common/job_creator';
|
||||
import { JobDetails } from './job_details';
|
||||
import { DetectorChart } from './detector_chart';
|
||||
import { JobProgress } from './components/job_progress';
|
||||
import { PostSaveOptions } from './components/post_save_options';
|
||||
|
||||
export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) => {
|
||||
const { jobCreator, jobValidator, jobValidatorUpdated, resultsLoader } = useContext(
|
||||
|
@ -24,7 +26,9 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
);
|
||||
const [progress, setProgress] = useState(resultsLoader.progress);
|
||||
const [showJsonFlyout, setShowJsonFlyout] = useState(false);
|
||||
const [creatingJob, setCreatingJob] = useState(false);
|
||||
const [isValid, setIsValid] = useState(jobValidator.validationSummary.basic);
|
||||
const [jobRunner, setJobRunner] = useState<JobRunner | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
jobCreator.subscribeToProgress(setProgress);
|
||||
|
@ -32,8 +36,10 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
|
||||
async function start() {
|
||||
setShowJsonFlyout(false);
|
||||
setCreatingJob(true);
|
||||
try {
|
||||
await jobCreator.createAndStartJob();
|
||||
const jr = await jobCreator.createAndStartJob();
|
||||
setJobRunner(jr);
|
||||
} catch (error) {
|
||||
// catch and display all job creation errors
|
||||
toastNotifications.addDanger({
|
||||
|
@ -42,6 +48,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
}),
|
||||
text: error.message,
|
||||
});
|
||||
setCreatingJob(false);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,13 +86,16 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
<Fragment>
|
||||
<EuiButton
|
||||
onClick={start}
|
||||
isDisabled={progress > 0}
|
||||
disabled={isValid === false}
|
||||
isDisabled={creatingJob === true || isValid === false}
|
||||
data-test-subj="mlJobWizardButtonCreateJob"
|
||||
>
|
||||
Create job
|
||||
</EuiButton>
|
||||
 
|
||||
</Fragment>
|
||||
)}
|
||||
{creatingJob === false && (
|
||||
<Fragment>
|
||||
<EuiButtonEmpty
|
||||
size="s"
|
||||
onClick={toggleJsonFlyout}
|
||||
|
@ -97,7 +107,6 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
{showJsonFlyout && (
|
||||
<JsonFlyout closeFlyout={() => setShowJsonFlyout(false)} jobCreator={jobCreator} />
|
||||
)}
|
||||
 
|
||||
</Fragment>
|
||||
)}
|
||||
{progress > 0 && (
|
||||
|
@ -105,6 +114,11 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
|
|||
<EuiButton onClick={viewResults} data-test-subj="mlJobWizardButtonViewResults">
|
||||
View results
|
||||
</EuiButton>
|
||||
{progress === 100 && (
|
||||
<Fragment>
|
||||
<PostSaveOptions jobRunner={jobRunner} />
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
)}
|
||||
</Fragment>
|
||||
|
|
|
@ -20,7 +20,12 @@ declare interface JobService {
|
|||
cloneJob(job: any): any;
|
||||
openJob(jobId: string): Promise<any>;
|
||||
saveNewDatafeed(datafeedConfig: any, jobId: string): Promise<any>;
|
||||
startDatafeed(datafeedId: string, jobId: string, start: number, end: number): Promise<any>;
|
||||
startDatafeed(
|
||||
datafeedId: string,
|
||||
jobId: string,
|
||||
start: number | undefined,
|
||||
end: number | undefined
|
||||
): Promise<any>;
|
||||
createResultsUrl(jobId: string[], start: number, end: number, location: string): string;
|
||||
getJobAndGroupIds(): ExistingJobsAndGroups;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue