diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js index 338222e3ac4a2..6118c620a3b9f 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.js @@ -36,6 +36,23 @@ export function loadFullJob(jobId) { }); } +export function loadClonableJob(jobId) { + return new Promise((resolve, reject) => { + ml.jobs + .jobs([jobId], true) + .then((jobs) => { + if (jobs.length) { + resolve(jobs[0]); + } else { + throw new Error(`Could not find job ${jobId}`); + } + }) + .catch((error) => { + reject(error); + }); + }); +} + export function isStartable(jobs) { return jobs.some( (j) => j.datafeedState === DATAFEED_STATE.STOPPED && j.jobState !== JOB_STATE.CLOSING @@ -180,10 +197,12 @@ function showResults(resp, action) { export async function cloneJob(jobId) { try { - const job = await loadFullJob(jobId); - if (job.custom_settings && job.custom_settings.created_by) { + const [job, originalJob] = await Promise.all([loadClonableJob(jobId), loadFullJob(jobId)]); + if (job.custom_settings && originalJob?.custom_settings?.created_by) { // if the job is from a wizards, i.e. contains a created_by property // use tempJobCloningObjects to temporarily store the job + job.custom_settings.created_by = originalJob?.custom_settings?.created_by; + mlJobService.tempJobCloningObjects.job = job; if ( @@ -214,6 +233,7 @@ export async function cloneJob(jobId) { // otherwise use the tempJobCloningObjects mlJobService.tempJobCloningObjects.job = job; } + mlJobService.tempJobCloningObjects.job = job; if (job.calendars) { mlJobService.tempJobCloningObjects.calendars = await mlCalendarService.fetchCalendarsByIds( diff --git a/x-pack/plugins/ml/public/application/services/job_service.js b/x-pack/plugins/ml/public/application/services/job_service.js index 5f504e4665500..317d18f306de3 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -329,26 +329,17 @@ class JobService { // create a deep copy of a job object // also remove items from the job which are set by the server and not needed // in the future this formatting could be optional + const tempJob = cloneDeep(job); // remove all of the items which should not be copied // such as counts, state and times - delete tempJob.state; - delete tempJob.job_version; + delete tempJob.calendars; delete tempJob.data_counts; - delete tempJob.create_time; - delete tempJob.finished_time; - delete tempJob.last_data_time; delete tempJob.model_size_stats; - delete tempJob.node; - delete tempJob.average_bucket_processing_time_ms; - delete tempJob.model_snapshot_id; - delete tempJob.open_time; - delete tempJob.established_model_memory; - delete tempJob.calendars; - delete tempJob.timing_stats; delete tempJob.forecasts_stats; - delete tempJob.assignment_explanation; + delete tempJob.state; + delete tempJob.timing_stats; delete tempJob.analysis_config.use_per_partition_normalization; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts index 10e035103dbec..5e8d06629c457 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/jobs.ts @@ -48,8 +48,8 @@ export const jobsApiProvider = (httpService: HttpService) => ({ }); }, - jobs(jobIds: string[]) { - const body = JSON.stringify({ jobIds }); + jobs(jobIds: string[], excludeGenerated?: boolean) { + const body = JSON.stringify({ jobIds, excludeGenerated }); return httpService.http({ path: `${basePath()}/jobs/jobs`, method: 'POST', diff --git a/x-pack/plugins/ml/server/models/job_service/jobs.ts b/x-pack/plugins/ml/server/models/job_service/jobs.ts index d47a1d4b4892d..3fcc4e7c70a12 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -257,7 +257,7 @@ export function jobsProvider(client: IScopedClusterClient, mlClient: MlClient) { return { jobs, jobsMap }; } - async function createFullJobsList(jobIds: string[] = []) { + async function createFullJobsList(jobIds: string[] = [], excludeGenerated = false) { const jobs: CombinedJobWithStats[] = []; const groups: { [jobId: string]: string[] } = {}; const datafeeds: { [id: string]: DatafeedWithStats } = {}; @@ -265,6 +265,9 @@ export function jobsProvider(client: IScopedClusterClient, mlClient: MlClient) { const globalCalendars: string[] = []; const jobIdsString = jobIds.join(); + + const datafeedParams = excludeGenerated ? { exclude_generated: excludeGenerated } : undefined; + const [ { body: jobResults }, { body: jobStatsResults }, @@ -273,11 +276,15 @@ export function jobsProvider(client: IScopedClusterClient, mlClient: MlClient) { calendarResults, latestBucketTimestampByJob, ] = await Promise.all([ - mlClient.getJobs(jobIds.length > 0 ? { job_id: jobIdsString } : undefined), + mlClient.getJobs( + jobIds.length > 0 + ? { job_id: jobIdsString, exclude_generated: excludeGenerated } + : undefined + ), mlClient.getJobStats( jobIds.length > 0 ? { job_id: jobIdsString } : undefined ), - mlClient.getDatafeeds(), + mlClient.getDatafeeds(datafeedParams), mlClient.getDatafeedStats(), calMngr.getAllCalendars(), getLatestBucketTimestampByJob(), diff --git a/x-pack/plugins/ml/server/routes/job_service.ts b/x-pack/plugins/ml/server/routes/job_service.ts index c067d9ce0abbc..e72d844338f49 100644 --- a/x-pack/plugins/ml/server/routes/job_service.ts +++ b/x-pack/plugins/ml/server/routes/job_service.ts @@ -294,8 +294,8 @@ export function jobServiceRoutes({ router, routeGuard }: RouteInitialization) { routeGuard.fullLicenseAPIGuard(async ({ client, mlClient, request, response }) => { try { const { createFullJobsList } = jobServiceProvider(client, mlClient); - const { jobIds } = request.body; - const resp = await createFullJobsList(jobIds); + const { jobIds, excludeGenerated } = request.body; + const resp = await createFullJobsList(jobIds, excludeGenerated); return response.ok({ body: resp, diff --git a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts index 583c9c41727ea..51bd32c7c25b9 100644 --- a/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts +++ b/x-pack/plugins/ml/server/routes/schemas/job_service_schema.ts @@ -42,6 +42,7 @@ export const forceStartDatafeedSchema = schema.object({ export const jobIdsSchema = schema.object({ /** Optional list of job IDs. */ jobIds: schema.maybe(schema.arrayOf(schema.maybe(schema.string()))), + excludeGenerated: schema.maybe(schema.boolean()), }); export const jobsWithTimerangeSchema = {