Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[7.x] [ML] Adding post create job options (#43205) #43289

Merged
merged 1 commit into from
Aug 14, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand All @@ -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({
Expand Down Expand Up @@ -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);
Original file line number Diff line number Diff line change
Expand Up @@ -430,7 +430,6 @@ export class JobsListView extends Component {
<CreateWatchFlyout
setShowFunction={this.setShowCreateWatchFlyoutFunction}
unsetShowFunction={this.unsetShowCreateWatchFlyoutFunction}
compile={this.props.compile}
/>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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';
Original file line number Diff line number Diff line change
@@ -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>
&emsp;
<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>
&emsp;
<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>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,35 @@ 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(
JobCreatorContext
);
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);
}, []);

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({
Expand All @@ -42,6 +48,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}),
text: error.message,
});
setCreatingJob(false);
}
}

Expand Down Expand Up @@ -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>
&emsp;
</Fragment>
)}
{creatingJob === false && (
<Fragment>
<EuiButtonEmpty
size="s"
onClick={toggleJsonFlyout}
Expand All @@ -97,14 +107,18 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
{showJsonFlyout && (
<JsonFlyout closeFlyout={() => setShowJsonFlyout(false)} jobCreator={jobCreator} />
)}
&emsp;
</Fragment>
)}
{progress > 0 && (
<Fragment>
<EuiButton onClick={viewResults} data-test-subj="mlJobWizardButtonViewResults">
View results
</EuiButton>
{progress === 100 && (
<Fragment>
<PostSaveOptions jobRunner={jobRunner} />
</Fragment>
)}
</Fragment>
)}
</Fragment>
Expand Down
7 changes: 6 additions & 1 deletion x-pack/legacy/plugins/ml/public/services/job_service.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down