Skip to content

Commit

Permalink
[ML] Adding option to create AD jobs without starting the datafeed (e…
Browse files Browse the repository at this point in the history
…lastic#77484)

* [ML] Adding option to create AD jobs without starting the datafeed

* changing translation id

* i just need some space

* adding missed spelling change

* disabling switch when running

* improving logic

* further test improvements

Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
  • Loading branch information
jgowdyelastic and elasticmachine committed Sep 17, 2020
1 parent 9355f5d commit bcc0d0b
Show file tree
Hide file tree
Showing 12 changed files with 273 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -268,8 +268,13 @@ export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToP
navigateToPath('/jobs/new_job');
}

export function advancedStartDatafeed(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) {
stashCombinedJob(jobCreator, false, false);
export function advancedStartDatafeed(
jobCreator: JobCreatorType | null,
navigateToPath: NavigateToPath
) {
if (jobCreator !== null) {
stashCombinedJob(jobCreator, false, false);
}
navigateToPath('/jobs');
}

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 { StartDatafeedSwitch } from './start_datafeed_switch';
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* 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 } from 'react';
import { i18n } from '@kbn/i18n';
import { EuiSwitch, EuiFormRow, EuiSpacer } from '@elastic/eui';
interface Props {
startDatafeed: boolean;
setStartDatafeed(start: boolean): void;
disabled?: boolean;
}

export const StartDatafeedSwitch: FC<Props> = ({
startDatafeed,
setStartDatafeed,
disabled = false,
}) => {
return (
<>
<EuiSpacer />
<EuiFormRow
helpText={i18n.translate(
'xpack.ml.newJob.wizard.summaryStep.startDatafeedCheckboxHelpText',
{
defaultMessage: 'If unselected, job can be started later from the jobs list.',
}
)}
>
<EuiSwitch
data-test-subj="mlJobWizardStartDatafeedCheckbox"
label={i18n.translate('xpack.ml.newJob.wizard.summaryStep.startDatafeedCheckbox', {
defaultMessage: 'Start immediately',
})}
checked={startDatafeed}
onChange={(e) => setStartDatafeed(e.target.checked)}
disabled={disabled}
/>
</EuiFormRow>
</>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { DatafeedDetails } from './components/datafeed_details';
import { DetectorChart } from './components/detector_chart';
import { JobProgress } from './components/job_progress';
import { PostSaveOptions } from './components/post_save_options';
import { StartDatafeedSwitch } from './components/start_datafeed_switch';
import { toastNotificationServiceProvider } from '../../../../../services/toast_notification_service';
import {
convertToAdvancedJob,
Expand All @@ -50,6 +51,7 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
const [creatingJob, setCreatingJob] = useState(false);
const [isValid, setIsValid] = useState(jobValidator.validationSummary.basic);
const [jobRunner, setJobRunner] = useState<JobRunner | null>(null);
const [startDatafeed, setStartDatafeed] = useState(true);

const isAdvanced = isAdvancedJobCreator(jobCreator);
const jsonEditorMode = isAdvanced ? EDITOR_MODE.EDITABLE : EDITOR_MODE.READONLY;
Expand All @@ -59,15 +61,17 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}, []);

async function start() {
setCreatingJob(true);
if (isAdvanced) {
await startAdvanced();
await createAdvancedJob();
} else if (startDatafeed === true) {
await createAndStartJob();
} else {
await startInline();
await createAdvancedJob(false);
}
}

async function startInline() {
setCreatingJob(true);
async function createAndStartJob() {
try {
const jr = await jobCreator.createAndStartJob();
setJobRunner(jr);
Expand All @@ -76,12 +80,11 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
}
}

async function startAdvanced() {
setCreatingJob(true);
async function createAdvancedJob(showStartModal: boolean = true) {
try {
await jobCreator.createJob();
await jobCreator.createDatafeed();
advancedStartDatafeed(jobCreator, navigateToPath);
advancedStartDatafeed(showStartModal ? jobCreator : null, navigateToPath);
} catch (error) {
handleJobCreationError(error);
}
Expand Down Expand Up @@ -131,6 +134,14 @@ export const SummaryStep: FC<StepProps> = ({ setCurrentStep, isCurrentStep }) =>
<EuiSpacer size="m" />
<JobDetails />

{isAdvanced === false && (
<StartDatafeedSwitch
startDatafeed={startDatafeed}
setStartDatafeed={setStartDatafeed}
disabled={creatingJob}
/>
)}

{isAdvanced && (
<Fragment>
<EuiHorizontalRule />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
Expand Down Expand Up @@ -230,7 +230,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Apr 5, 2019 @ 11:25:35.770',
'Nov 21, 2019 @ 06:01:13.914'
Expand Down
1 change: 1 addition & 0 deletions x-pack/test/functional/apps/ml/anomaly_detection/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default function ({ loadTestFile }: FtrProviderContext) {
this.tags(['skipFirefox']);

loadTestFile(require.resolve('./single_metric_job'));
loadTestFile(require.resolve('./single_metric_job_without_datafeed_start'));
loadTestFile(require.resolve('./multi_metric_job'));
loadTestFile(require.resolve('./population_job'));
loadTestFile(require.resolve('./saved_search_job'));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down Expand Up @@ -235,7 +235,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
Expand Down Expand Up @@ -261,7 +261,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Jun 12, 2019 @ 00:04:19.000',
'Jul 12, 2019 @ 23:45:36.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the timerange');
await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down Expand Up @@ -212,7 +212,7 @@ export default function ({ getService }: FtrProviderContext) {
await ml.testExecution.logTestStep('job cloning displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job cloning sets the timerange');
await ml.testExecution.logTestStep('job cloning sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* 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 { FtrProviderContext } from '../../../ftr_provider_context';

export default function ({ getService }: FtrProviderContext) {
const esArchiver = getService('esArchiver');
const ml = getService('ml');

const jobId = `fq_single_1_${Date.now()}`;
const aggAndFieldIdentifier = 'Mean(responsetime)';
const bucketSpan = '30m';

function getExpectedRow(expectedJobId: string) {
return {
id: expectedJobId,
description: '',
jobGroups: [],
recordCount: '0',
memoryStatus: 'ok',
jobState: 'closed',
datafeedState: 'stopped',
latestTimestamp: '',
};
}

function getExpectedCounts(expectedJobId: string) {
return {
job_id: expectedJobId,
processed_record_count: '0',
processed_field_count: '0',
input_bytes: '0.0 B',
input_field_count: '0',
invalid_date_count: '0',
missing_field_count: '0',
out_of_order_timestamp_count: '0',
empty_bucket_count: '0',
sparse_bucket_count: '0',
bucket_count: '0',
};
}

function getExpectedModelSizeStats(expectedJobId: string) {
return {
job_id: expectedJobId,
result_type: 'model_size_stats',
total_by_field_count: '0',
total_over_field_count: '0',
total_partition_field_count: '0',
bucket_allocation_failures_count: '0',
memory_status: 'ok',
};
}

describe('single metric without datafeed start', function () {
this.tags(['mlqa']);
before(async () => {
await esArchiver.loadIfNeeded('ml/farequote');
await ml.testResources.createIndexPatternIfNeeded('ft_farequote', '@timestamp');
await ml.testResources.setKibanaTimeZoneToUTC();

await ml.securityUI.loginAsMlPowerUser();
});

after(async () => {
await ml.api.cleanMlIndices();
});

it('job creation loads the single metric wizard for the source data', async () => {
await ml.testExecution.logTestStep('job creation loads the job management page');
await ml.navigation.navigateToMl();
await ml.navigation.navigateToJobManagement();

await ml.testExecution.logTestStep('job creation loads the new job source selection page');
await ml.jobManagement.navigateToNewJobSourceSelection();

await ml.testExecution.logTestStep('job creation loads the job type selection page');
await ml.jobSourceSelection.selectSourceForAnomalyDetectionJob('ft_farequote');

await ml.testExecution.logTestStep('job creation loads the single metric job wizard page');
await ml.jobTypeSelection.selectSingleMetricJob();
});

it('job creation navigates through the single metric wizard and sets all needed fields', async () => {
await ml.testExecution.logTestStep('job creation displays the time range step');
await ml.jobWizardCommon.assertTimeRangeSectionExists();

await ml.testExecution.logTestStep('job creation sets the time range');
await ml.jobWizardCommon.clickUseFullDataButton(
'Feb 7, 2016 @ 00:00:00.000',
'Feb 11, 2016 @ 23:59:54.000'
);

await ml.testExecution.logTestStep('job creation displays the event rate chart');
await ml.jobWizardCommon.assertEventRateChartExists();
await ml.jobWizardCommon.assertEventRateChartHasData();

await ml.testExecution.logTestStep('job creation displays the pick fields step');
await ml.jobWizardCommon.advanceToPickFieldsSection();

await ml.testExecution.logTestStep('job creation selects field and aggregation');
await ml.jobWizardCommon.assertAggAndFieldInputExists();
await ml.jobWizardCommon.selectAggAndField(aggAndFieldIdentifier, true);
await ml.jobWizardCommon.assertAnomalyChartExists('LINE');

await ml.testExecution.logTestStep('job creation inputs the bucket span');
await ml.jobWizardCommon.assertBucketSpanInputExists();
await ml.jobWizardCommon.setBucketSpan(bucketSpan);

await ml.testExecution.logTestStep('job creation displays the job details step');
await ml.jobWizardCommon.advanceToJobDetailsSection();

await ml.testExecution.logTestStep('job creation inputs the job id');
await ml.jobWizardCommon.assertJobIdInputExists();
await ml.jobWizardCommon.setJobId(jobId);

await ml.testExecution.logTestStep('job creation displays the validation step');
await ml.jobWizardCommon.advanceToValidationSection();

await ml.testExecution.logTestStep('job creation displays the summary step');
await ml.jobWizardCommon.advanceToSummarySection();
});

it('job creation runs the job and displays it correctly in the job list', async () => {
await ml.testExecution.logTestStep('job creation creates the job and finishes processing');

await ml.jobWizardCommon.assertStartDatafeedSwitchExists();
await ml.jobWizardCommon.toggleStartDatafeedSwitch(false);

await ml.jobWizardCommon.assertCreateJobButtonExists();
await ml.jobWizardCommon.createJobWithoutDatafeedStart();

await ml.jobTable.waitForJobsToLoad();
await ml.jobTable.filterWithSearchString(jobId, 1);

await ml.testExecution.logTestStep(
'job creation displays details for the created job in the job list'
);
await ml.jobTable.assertJobRowFields(jobId, getExpectedRow(jobId));

await ml.jobTable.assertJobRowDetailsCounts(
jobId,
getExpectedCounts(jobId),
getExpectedModelSizeStats(jobId)
);
});
});
}
Loading

0 comments on commit bcc0d0b

Please sign in to comment.