From 2d40fa3311bf9dbd9fd7259c65220d29c6d42cc5 Mon Sep 17 00:00:00 2001 From: James Gowdy Date: Tue, 10 Sep 2024 17:58:14 +0100 Subject: [PATCH] [ML] Job service refactor (#191724) Follow on from dependancy cache removal https://github.com/elastic/kibana/pull/189729 Moving and removing as much as possible out of the `JobService` class. - Moving the job cloning functions to a separate class as they do not require the same dependancies as `JobService` - Removing api basic wrapper functions like `saveNewJob`, `openJob`, `forceStartDatafeeds` etc - Removes toast dependency. This might be controversial, but I think it is unnecessary for these basic job/datafeed loading functions to show a toast if they fail. If we cannot load the basic list of jobs then there is something else very wrong with the system. We also throw the errors, so they will not be lost. It should be up to the calling function to display a toast. Removing this dependency cleans up the code quite a lot. - The `JobCreator` classes no longer use `JobService` --- .../annotations_table/annotations_table.js | 6 +- .../components/anomalies_table/links_menu.tsx | 2 +- .../custom_hooks/use_create_ad_links.ts | 7 +- .../import_jobs_flyout/jobs_import_service.ts | 3 +- .../rule_editor/rule_editor_flyout.js | 5 +- .../close_jobs_confirm_modal.tsx | 7 +- .../stop_datafeeds_confirm_modal.tsx | 7 +- .../delete_job_modal/delete_job_modal.tsx | 11 +- .../edit_job_flyout/tabs/detectors.js | 9 - .../components/job_actions/management.js | 7 +- .../components/jobs_list/jobs_list.js | 7 - .../jobs_list_view/jobs_list_view.js | 12 +- .../multi_job_actions/actions_menu.js | 19 +- .../group_selector/group_selector.js | 4 +- .../reset_job_modal/reset_job_modal.tsx | 11 +- .../start_datafeed_modal.js | 10 +- .../jobs/jobs_list/components/utils.d.ts | 17 +- .../jobs/jobs_list/components/utils.js | 71 ++-- .../job_creator/advanced_job_creator.ts | 4 +- .../job_creator/categorization_job_creator.ts | 4 +- .../common/job_creator/geo_job_creator.ts | 4 +- .../new_job/common/job_creator/job_creator.ts | 29 +- .../common/job_creator/job_creator_factory.ts | 4 +- .../job_creator/multi_metric_job_creator.ts | 4 +- .../job_creator/population_job_creator.ts | 4 +- .../common/job_creator/rare_job_creator.ts | 4 +- .../job_creator/single_metric_job_creator.ts | 4 +- .../common/job_creator/util/general.ts | 43 ++- .../new_job/common/job_runner/job_runner.ts | 14 +- .../quick_create_job_base.ts | 4 +- .../new_job/job_from_lens/quick_create_job.ts | 9 +- .../new_job/job_from_lens/route_resolver.ts | 7 +- .../new_job/job_from_map/quick_create_job.ts | 9 +- .../new_job/job_from_map/route_resolver.ts | 7 +- .../quick_create_job.ts | 9 +- .../route_resolver.ts | 7 +- .../components/data_view/change_data_view.tsx | 5 +- .../single_metric_view/settings.tsx | 4 +- .../pages/components/summary_step/summary.tsx | 11 +- .../preconfigured_job_redirect.ts | 5 +- .../jobs/new_job/pages/new_job/page.tsx | 43 +-- .../routing/routes/new_job/from_lens.tsx | 3 - .../routing/routes/new_job/from_map.tsx | 4 +- .../routes/new_job/from_pattern_analysis.tsx | 3 - .../routes/new_job/index_or_search.tsx | 4 +- .../routing/routes/new_job/recognize.tsx | 6 +- .../routing/routes/new_job/wizard.tsx | 7 +- .../services/job_cloning_service.ts | 130 +++++++ .../application/services/job_service.d.ts | 45 +-- .../application/services/job_service.js | 325 +----------------- .../services/ml_api_service/index.ts | 7 + .../timeseriesexplorer/timeseriesexplorer.js | 2 +- .../public/application/util/get_services.ts | 4 +- .../ml/public/application/util/results_url.ts | 95 +++++ ...et_anomaly_charts_services_dependencies.ts | 5 +- .../job_creation/aiops/flyout/create_job.tsx | 11 +- .../layer/compatible_layer.tsx | 8 +- .../layer/compatible_layer.tsx | 8 +- .../single_metric_viewer/get_services.ts | 2 +- .../ml/server/models/job_service/jobs.ts | 3 +- 60 files changed, 417 insertions(+), 708 deletions(-) create mode 100644 x-pack/plugins/ml/public/application/services/job_cloning_service.ts create mode 100644 x-pack/plugins/ml/public/application/util/results_url.ts diff --git a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js index 31ac00c1f79db9..95433e3ec7c06e 100644 --- a/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js +++ b/x-pack/plugins/ml/public/application/components/annotations/annotations_table/annotations_table.js @@ -33,7 +33,6 @@ import { withKibana } from '@kbn/kibana-react-plugin/public'; import { addItemToRecentlyAccessed } from '../../../util/recently_accessed'; import { mlJobServiceFactory } from '../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../services/toast_notification_service'; import { mlTableService } from '../../../services/table_service'; import { ANNOTATIONS_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../common/constants/search'; import { @@ -101,10 +100,7 @@ class AnnotationsTableUI extends Component { this.sorting = { sort: { field: 'timestamp', direction: 'asc' }, }; - this.mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(props.kibana.services.notifications.toasts), - props.kibana.services.mlServices.mlApi - ); + this.mlJobService = mlJobServiceFactory(props.kibana.services.mlServices.mlApi); } getAnnotations() { diff --git a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx index ccf9083c907912..42cf24b52cac48 100644 --- a/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx +++ b/x-pack/plugins/ml/public/application/components/anomalies_table/links_menu.tsx @@ -272,7 +272,7 @@ export const LinksMenuUI = (props: LinksMenuProps) => { if (dataView === null) { return; } - + dataView.getIndexPattern(); const field = findMessageField(dataView); if (field !== null) { setMessageField(field); diff --git a/x-pack/plugins/ml/public/application/components/custom_hooks/use_create_ad_links.ts b/x-pack/plugins/ml/public/application/components/custom_hooks/use_create_ad_links.ts index 50a4c7f1efda96..c2534eb66cbeca 100644 --- a/x-pack/plugins/ml/public/application/components/custom_hooks/use_create_ad_links.ts +++ b/x-pack/plugins/ml/public/application/components/custom_hooks/use_create_ad_links.ts @@ -11,7 +11,7 @@ import { ANOMALY_DETECTION_DEFAULT_TIME_RANGE, ANOMALY_DETECTION_ENABLE_TIME_RANGE, } from '../../../../common/constants/settings'; -import { useMlJobService } from '../../services/job_service'; +import { createResultsUrlForJobs } from '../../util/results_url'; export const useCreateADLinks = () => { const { @@ -19,13 +19,12 @@ export const useCreateADLinks = () => { http: { basePath }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const useUserTimeSettings = useUiSettings().get(ANOMALY_DETECTION_ENABLE_TIME_RANGE); const userTimeSettings = useUiSettings().get(ANOMALY_DETECTION_DEFAULT_TIME_RANGE); const createLinkWithUserDefaults = useCallback( - (location: any, jobList: any) => { - const resultsUrl = mlJobService.createResultsUrlForJobs( + (location, jobList) => { + const resultsUrl = createResultsUrlForJobs( jobList, location, useUserTimeSettings === true && userTimeSettings !== undefined diff --git a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/jobs_import_service.ts b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/jobs_import_service.ts index 50c3c6df0fe5a7..dd9b0fed05aa87 100644 --- a/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/jobs_import_service.ts +++ b/x-pack/plugins/ml/public/application/components/import_export_jobs/import_jobs_flyout/jobs_import_service.ts @@ -7,6 +7,7 @@ import type { DataFrameAnalyticsConfig } from '@kbn/ml-data-frame-analytics-utils'; +import { createDatafeedId } from '../../../../../common/util/job_utils'; import type { JobType } from '../../../../../common/types/saved_objects'; import type { Job, Datafeed } from '../../../../../common/types/anomaly_detection_jobs'; import type { Filter } from '../../../../../common/types/filters'; @@ -105,7 +106,7 @@ export class JobImportService { const { jobId } = jobIds[i]; j.job.job_id = jobId; j.datafeed.job_id = jobId; - j.datafeed.datafeed_id = `datafeed-${jobId}`; + j.datafeed.datafeed_id = createDatafeedId(jobId); return j; }); } diff --git a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js index 59f1fd3d398180..7ff2eb04c4ccdf 100644 --- a/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js +++ b/x-pack/plugins/ml/public/application/components/rule_editor/rule_editor_flyout.js @@ -81,10 +81,7 @@ class RuleEditorFlyoutUI extends Component { this.partitioningFieldNames = []; this.canGetFilters = checkPermission('canGetFilters'); - this.mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(props.kibana.services.notifications.toasts), - props.kibana.services.mlServices.mlApi - ); + this.mlJobService = mlJobServiceFactory(props.kibana.services.mlServices.mlApi); } componentDidMount() { diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/close_jobs_confirm_modal.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/close_jobs_confirm_modal.tsx index 605ebc0a412ea3..45a94e618b12a6 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/close_jobs_confirm_modal.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/close_jobs_confirm_modal.tsx @@ -19,10 +19,9 @@ import { EuiButton, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useMlKibana } from '../../../../contexts/kibana'; +import { useMlApi, useMlKibana } from '../../../../contexts/kibana'; import type { MlSummaryJob } from '../../../../../../common/types/anomaly_detection_jobs'; import { isManagedJob } from '../../../jobs_utils'; -import { useMlJobService } from '../../../../services/job_service'; import { closeJobs } from '../utils'; import { ManagedJobsWarningCallout } from './managed_jobs_warning_callout'; @@ -44,7 +43,7 @@ export const CloseJobsConfirmModal: FC = ({ notifications: { toasts }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); + const mlApi = useMlApi(); const [modalVisible, setModalVisible] = useState(false); const [hasManagedJob, setHasManaged] = useState(true); const [jobsToReset, setJobsToReset] = useState([]); @@ -121,7 +120,7 @@ export const CloseJobsConfirmModal: FC = ({ { - closeJobs(toasts, mlJobService, jobsToReset, refreshJobs); + closeJobs(toasts, mlApi, jobsToReset, refreshJobs); closeModal(); }} fill diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/stop_datafeeds_confirm_modal.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/stop_datafeeds_confirm_modal.tsx index 265e0c58986aa6..6d12deb9ebaed8 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/stop_datafeeds_confirm_modal.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/confirm_modals/stop_datafeeds_confirm_modal.tsx @@ -19,10 +19,9 @@ import { EuiButton, } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useMlKibana } from '../../../../contexts/kibana'; +import { useMlApi, useMlKibana } from '../../../../contexts/kibana'; import type { MlSummaryJob } from '../../../../../../common/types/anomaly_detection_jobs'; import { isManagedJob } from '../../../jobs_utils'; -import { useMlJobService } from '../../../../services/job_service'; import { stopDatafeeds } from '../utils'; import { ManagedJobsWarningCallout } from './managed_jobs_warning_callout'; @@ -45,7 +44,7 @@ export const StopDatafeedsConfirmModal: FC = ({ notifications: { toasts }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); + const mlApi = useMlApi(); const [modalVisible, setModalVisible] = useState(false); const [hasManagedJob, setHasManaged] = useState(true); const [jobsToStop, setJobsToStop] = useState([]); @@ -122,7 +121,7 @@ export const StopDatafeedsConfirmModal: FC = ({ { - stopDatafeeds(toasts, mlJobService, jobsToStop, refreshJobs); + stopDatafeeds(toasts, mlApi, jobsToStop, refreshJobs); closeModal(); }} fill diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/delete_job_modal/delete_job_modal.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/delete_job_modal/delete_job_modal.tsx index 75d3fb270d226b..c8296c5afbef71 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/delete_job_modal/delete_job_modal.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/delete_job_modal/delete_job_modal.tsx @@ -23,11 +23,10 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; -import { useMlKibana } from '../../../../contexts/kibana'; +import { useMlApi, useMlKibana } from '../../../../contexts/kibana'; import { deleteJobs } from '../utils'; import { BLOCKED_JOBS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants/jobs_list'; import { DeleteSpaceAwareItemCheckModal } from '../../../../components/delete_space_aware_item_check_modal'; -import { useMlJobService } from '../../../../services/job_service'; import type { MlSummaryJob } from '../../../../../../common/types/anomaly_detection_jobs'; import { isManagedJob } from '../../../jobs_utils'; import { ManagedJobsWarningCallout } from '../confirm_modals/managed_jobs_warning_callout'; @@ -46,7 +45,7 @@ export const DeleteJobModal: FC = ({ setShowFunction, unsetShowFunction, notifications: { toasts }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); + const mlApi = useMlApi(); const [deleting, setDeleting] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [adJobs, setAdJobs] = useState([]); @@ -92,7 +91,7 @@ export const DeleteJobModal: FC = ({ setShowFunction, unsetShowFunction, setDeleting(true); deleteJobs( toasts, - mlJobService, + mlApi, jobIds.map((id) => ({ id })), deleteUserAnnotations, deleteAlertingRules @@ -102,9 +101,7 @@ export const DeleteJobModal: FC = ({ setShowFunction, unsetShowFunction, closeModal(); refreshJobs(); }, BLOCKED_JOBS_REFRESH_INTERVAL_MS); - // exclude mlJobservice from deps - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [jobIds, deleteUserAnnotations, deleteAlertingRules, closeModal, refreshJobs]); + }, [toasts, mlApi, jobIds, deleteUserAnnotations, deleteAlertingRules, closeModal, refreshJobs]); if (modalVisible === false || jobIds.length === 0) { return null; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js index daf9e7996b5c82..0dcc57d30fb016 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/edit_job_flyout/tabs/detectors.js @@ -13,8 +13,6 @@ import { EuiFieldText, EuiForm, EuiFormRow, EuiSpacer, EuiTitle } from '@elastic import { FormattedMessage } from '@kbn/i18n-react'; import { context } from '@kbn/kibana-react-plugin/public'; -import { mlJobServiceFactory } from '../../../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../../../services/toast_notification_service'; import { detectorToString } from '../../../../../util/string_utils'; export class Detectors extends Component { @@ -23,13 +21,6 @@ export class Detectors extends Component { constructor(props, constructorContext) { super(props, constructorContext); - const mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(constructorContext.services.notifications.toasts), - constructorContext.services.mlServices.mlApi - ); - - this.detectors = mlJobService.getJobGroups().map((g) => ({ label: g.id })); - this.state = { detectors: [], detectorDescriptions: [], diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js index eac225f2db9909..4d23114eea9f74 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/job_actions/management.js @@ -25,7 +25,6 @@ export function actionsMenuContent( toastNotifications, application, mlApi, - mlJobService, showEditJobFlyout, showDatafeedChartFlyout, showDeleteJobModal, @@ -77,7 +76,7 @@ export function actionsMenuContent( if (isManagedJob(item)) { showStopDatafeedsConfirmModal([item]); } else { - stopDatafeeds(toastNotifications, mlJobService, [item], refreshJobs); + stopDatafeeds(toastNotifications, mlApi, [item], refreshJobs); } closeMenu(true); @@ -114,7 +113,7 @@ export function actionsMenuContent( if (isManagedJob(item)) { showCloseJobsConfirmModal([item]); } else { - closeJobs(toastNotifications, mlJobService, [item], refreshJobs); + closeJobs(toastNotifications, mlApi, [item], refreshJobs); } closeMenu(true); @@ -153,7 +152,7 @@ export function actionsMenuContent( return isJobBlocked(item) === false && canCreateJob; }, onClick: (item) => { - cloneJob(toastNotifications, application, mlApi, mlJobService, item.id); + cloneJob(toastNotifications, application, mlApi, item.id); closeMenu(true); }, 'data-test-subj': 'mlActionButtonCloneJob', diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js index f6deecc3ba309c..2a82cd10afd574 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list/jobs_list.js @@ -32,8 +32,6 @@ import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; import { AnomalyDetectionJobIdLink } from './job_id_link'; import { isManagedJob } from '../../../jobs_utils'; -import { mlJobServiceFactory } from '../../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; const PAGE_SIZE_OPTIONS = [10, 25, 50]; @@ -47,10 +45,6 @@ export class JobsListUI extends Component { }; this.mlApi = props.kibana.services.mlServices.mlApi; - this.mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(props.kibana.services.notifications.toasts), - this.mlApi - ); } static getDerivedStateFromProps(props) { @@ -341,7 +335,6 @@ export class JobsListUI extends Component { this.props.kibana.services.notifications.toasts, this.props.kibana.services.application, this.mlApi, - this.mlJobService, this.props.showEditJobFlyout, this.props.showDatafeedChartFlyout, this.props.showDeleteJobModal, diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js index 6693f9bc22ab10..b80ad9bcc0e5af 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/jobs_list_view/jobs_list_view.js @@ -10,7 +10,7 @@ import { EuiFlexGroup, EuiFlexItem, EuiSpacer } from '@elastic/eui'; import { withKibana } from '@kbn/kibana-react-plugin/public'; -import { checkForAutoStartDatafeed, filterJobs, loadFullJob } from '../utils'; +import { filterJobs, loadFullJob } from '../utils'; import { JobsList } from '../jobs_list'; import { JobDetails } from '../job_details'; import { JobFilterBar } from '../job_filter_bar'; @@ -37,8 +37,7 @@ import { StopDatafeedsConfirmModal } from '../confirm_modals/stop_datafeeds_conf import { CloseJobsConfirmModal } from '../confirm_modals/close_jobs_confirm_modal'; import { AnomalyDetectionEmptyState } from '../anomaly_detection_empty_state'; import { removeNodeInfo } from '../../../../../../common/util/job_utils'; -import { mlJobServiceFactory } from '../../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; +import { jobCloningService } from '../../../../services/job_cloning_service'; let blockingJobsRefreshTimeout = null; @@ -80,11 +79,6 @@ export class JobsListViewUI extends Component { * @private */ this._isFiltersSet = false; - - this.mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(props.kibana.services.notifications.toasts), - props.kibana.services.mlServices.mlApi - ); } componentDidMount() { @@ -106,7 +100,7 @@ export class JobsListViewUI extends Component { } openAutoStartDatafeedModal() { - const job = checkForAutoStartDatafeed(this.mlJobService); + const job = jobCloningService.checkForAutoStartDatafeed(); if (job !== undefined) { this.showStartDatafeedModal([job]); } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js index a8bd2789e54330..02eef7d83f5b04 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/actions_menu.js @@ -16,8 +16,6 @@ import { context } from '@kbn/kibana-react-plugin/public'; import { checkPermission } from '../../../../capabilities/check_capabilities'; import { mlNodesAvailable } from '../../../../ml_nodes_check/check_ml_nodes'; -import { mlJobServiceFactory } from '../../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; import { isManagedJob } from '../../../jobs_utils'; @@ -46,12 +44,8 @@ class MultiJobActionsMenuUI extends Component { this.canResetJob = checkPermission('canResetJob') && mlNodesAvailable(); this.canCreateMlAlerts = checkPermission('canCreateMlAlerts'); - this.toastNoticiations = constructorContext.services.notifications.toasts; - const mlApi = constructorContext.services.mlServices.mlApi; - const toastNotificationService = toastNotificationServiceProvider( - constructorContext.services.notifications.toasts - ); - this.mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); + this.toastNotifications = constructorContext.services.notifications.toasts; + this.mlApi = constructorContext.services.mlServices.mlApi; } onButtonClick = () => { @@ -116,7 +110,7 @@ class MultiJobActionsMenuUI extends Component { if (this.props.jobs.some((j) => isManagedJob(j))) { this.props.showCloseJobsConfirmModal(this.props.jobs); } else { - closeJobs(this.toastNotifications, this.mlJobService, this.props.jobs); + closeJobs(this.toastNotifications, this.mlApi, this.props.jobs); } this.closePopover(); @@ -163,7 +157,12 @@ class MultiJobActionsMenuUI extends Component { if (this.props.jobs.some((j) => isManagedJob(j))) { this.props.showStopDatafeedsConfirmModal(this.props.jobs); } else { - stopDatafeeds(this.props.jobs, this.props.refreshJobs); + stopDatafeeds( + this.toastNotifications, + this.mlApi, + this.props.jobs, + this.props.refreshJobs + ); } this.closePopover(); }} diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js index 6248915b87d4b1..9468f586c60047 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/multi_job_actions/group_selector/group_selector.js @@ -158,8 +158,8 @@ export class GroupSelectorUI extends Component { } const tempJobs = newJobs.map((j) => ({ jobId: j.id, groups: j.newGroups })); - const ml = this.props.kibana.services.mlServices.mlApi; - ml.jobs + const mlApi = this.props.kibana.services.mlServices.mlApi; + mlApi.jobs .updateGroups(tempJobs) .then((resp) => { let success = true; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/reset_job_modal/reset_job_modal.tsx b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/reset_job_modal/reset_job_modal.tsx index 1658c428d9b009..cce101c9305e5c 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/reset_job_modal/reset_job_modal.tsx +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/reset_job_modal/reset_job_modal.tsx @@ -25,8 +25,7 @@ import { i18n } from '@kbn/i18n'; import { resetJobs } from '../utils'; import type { MlSummaryJob } from '../../../../../../common/types/anomaly_detection_jobs'; import { RESETTING_JOBS_REFRESH_INTERVAL_MS } from '../../../../../../common/constants/jobs_list'; -import { useMlKibana } from '../../../../contexts/kibana'; -import { useMlJobService } from '../../../../services/job_service'; +import { useMlApi, useMlKibana } from '../../../../contexts/kibana'; import { OpenJobsWarningCallout } from './open_jobs_warning_callout'; import { isManagedJob } from '../../../jobs_utils'; import { ManagedJobsWarningCallout } from '../confirm_modals/managed_jobs_warning_callout'; @@ -45,7 +44,7 @@ export const ResetJobModal: FC = ({ setShowFunction, unsetShowFunction, r notifications: { toasts }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); + const mlApi = useMlApi(); const [resetting, setResetting] = useState(false); const [modalVisible, setModalVisible] = useState(false); const [jobIds, setJobIds] = useState([]); @@ -81,14 +80,12 @@ export const ResetJobModal: FC = ({ setShowFunction, unsetShowFunction, r const resetJob = useCallback(async () => { setResetting(true); - await resetJobs(toasts, mlJobService, jobIds, deleteUserAnnotations); + await resetJobs(toasts, mlApi, jobIds, deleteUserAnnotations); closeModal(); setTimeout(() => { refreshJobs(); }, RESETTING_JOBS_REFRESH_INTERVAL_MS); - // exclude mlJobservice from deps - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [closeModal, deleteUserAnnotations, jobIds, refreshJobs]); + }, [closeModal, deleteUserAnnotations, jobIds, mlApi, refreshJobs, toasts]); if (modalVisible === false || jobIds.length === 0) { return null; diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js index db5f28a5e242b5..1f3134fbf56ea2 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/start_datafeed_modal/start_datafeed_modal.js @@ -24,9 +24,6 @@ import { import { FormattedMessage } from '@kbn/i18n-react'; import { context } from '@kbn/kibana-react-plugin/public'; -import { mlJobServiceFactory } from '../../../../services/job_service'; -import { toastNotificationServiceProvider } from '../../../../services/toast_notification_service'; - import { isManagedJob } from '../../../jobs_utils'; import { forceStartDatafeeds } from '../utils'; @@ -57,10 +54,7 @@ export class StartDatafeedModal extends Component { this.refreshJobs = this.props.refreshJobs; this.getShowCreateAlertFlyoutFunction = this.props.getShowCreateAlertFlyoutFunction; this.toastNotifications = constructorContext.services.notifications.toasts; - this.mlJobService = mlJobServiceFactory( - toastNotificationServiceProvider(this.toastNotifications), - constructorContext.services.mlServices.mlApi - ); + this.mlApi = constructorContext.services.mlServices.mlApi; } componentDidMount() { @@ -125,7 +119,7 @@ export class StartDatafeedModal extends Component { ? this.state.endTime.valueOf() : this.state.endTime; - forceStartDatafeeds(this.toastNotifications, this.mlJobService, jobs, start, end, () => { + forceStartDatafeeds(this.toastNotifications, this.mlApi, jobs, start, end, () => { if (this.state.createAlert && jobs.length > 0) { this.getShowCreateAlertFlyoutFunction()(jobs.map((job) => job.id)); } diff --git a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts index bd6f60a0500476..d1f699bb20f3cf 100644 --- a/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts +++ b/x-pack/plugins/ml/public/application/jobs/jobs_list/components/utils.d.ts @@ -12,7 +12,6 @@ import type { CombinedJobWithStats, MlSummaryJob, } from '../../../../../common/types/anomaly_detection_jobs'; -import type { MlJobService } from '../../../services/job_service'; import type { MlApi } from '../../../services/ml_api_service'; export function loadFullJob(mlApi: MlApi, jobId: string): Promise; @@ -22,7 +21,7 @@ export function isClosable(jobs: CombinedJobWithStats[]): boolean; export function isResettable(jobs: CombinedJobWithStats[]): boolean; export function forceStartDatafeeds( toastNotifications: ToastsStart, - mlJobService: MlJobService, + mlApi: MlApi, jobs: CombinedJobWithStats[], start: number | undefined, end: number | undefined, @@ -30,7 +29,7 @@ export function forceStartDatafeeds( ): Promise; export function stopDatafeeds( toastNotifications: ToastsStart, - mlJobService: MlJobService, + mlApi: MlApi, jobs: CombinedJobWithStats[] | MlSummaryJob[], finish?: () => void ): Promise; @@ -43,18 +42,17 @@ export function cloneJob( toastNotifications: ToastsStart, application: ApplicationStart, mlApi: MlApi, - mlJobService: MlJobService, jobId: string ): Promise; export function closeJobs( toastNotifications: ToastsStart, - mlJobService: MlJobService, + mlApi: MlApi, jobs: CombinedJobWithStats[] | MlSummaryJob[], finish?: () => void ): Promise; export function deleteJobs( toastNotifications: ToastsStart, - mlJobService: MlJobService, + mlApi: MlApi, jobs: Array<{ id: string }>, deleteUserAnnotations?: boolean, deleteAlertingRules?: boolean, @@ -62,7 +60,7 @@ export function deleteJobs( ): Promise; export function resetJobs( toastNotifications: ToastsStart, - mlJobService: MlJobService, + mlApi: MlApi, jobIds: string[], deleteUserAnnotations?: boolean, finish?: () => void @@ -73,8 +71,3 @@ export function filterJobs( ): CombinedJobWithStats[]; export function jobProperty(job: CombinedJobWithStats, prop: string): any; export function jobTagFilter(jobs: CombinedJobWithStats[], value: string): CombinedJobWithStats[]; -export function checkForAutoStartDatafeed( - mlJobService: MlJobService -): - | { id: string; hasDatafeed: boolean; latestTimestampSortValue: number; datafeedId: string } - | undefined; 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 36a070b851147d..8e457e8b1e8000 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 @@ -14,7 +14,7 @@ import { JOB_STATE, DATAFEED_STATE } from '../../../../../common/constants/state import { JOB_ACTION } from '../../../../../common/constants/job_actions'; import { parseInterval } from '../../../../../common/util/parse_interval'; import { mlCalendarService } from '../../../services/calendar_service'; -import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { jobCloningService } from '../../../services/job_cloning_service'; import { ML_PAGES } from '../../../../../common/constants/locator'; import { PLUGIN_ID } from '../../../../../common/constants/app'; import { CREATED_BY_LABEL } from '../../../../../common/constants/new_job'; @@ -82,14 +82,14 @@ export function isResettable(jobs) { export function forceStartDatafeeds( toastNotifications, - mlJobService, + mlApi, jobs, start, end, finish = () => {} ) { const datafeedIds = jobs.filter((j) => j.hasDatafeed).map((j) => j.datafeedId); - mlJobService + mlApi.jobs .forceStartDatafeeds(datafeedIds, start, end) .then((resp) => { showResults(toastNotifications, resp, DATAFEED_STATE.STARTED); @@ -106,9 +106,9 @@ export function forceStartDatafeeds( }); } -export function stopDatafeeds(toastNotifications, mlJobService, jobs, finish = () => {}) { +export function stopDatafeeds(toastNotifications, mlApi, jobs, finish = () => {}) { const datafeedIds = jobs.filter((j) => j.hasDatafeed).map((j) => j.datafeedId); - mlJobService + mlApi.jobs .stopDatafeeds(datafeedIds) .then((resp) => { showResults(toastNotifications, resp, DATAFEED_STATE.STOPPED); @@ -214,13 +214,17 @@ function showResults(toastNotifications, resp, action) { } } -export async function cloneJob(toastNotifications, application, mlApi, mlJobService, jobId) { +export async function cloneJob(toastNotifications, application, mlApi, jobId) { try { const [{ job: cloneableJob, datafeed }, originalJob] = await Promise.all([ loadJobForCloning(mlApi, jobId), loadFullJob(mlApi, jobId), ]); + const tempJobCloningData = { + skipTimeRangeStep: false, + }; + const createdBy = originalJob?.custom_settings?.created_by; if ( cloneableJob !== undefined && @@ -228,9 +232,9 @@ export async function cloneJob(toastNotifications, application, mlApi, mlJobServ createdBy !== CREATED_BY_LABEL.ADVANCED ) { // if the job is from a wizards, i.e. contains a created_by property - // use tempJobCloningObjects to temporarily store the job - mlJobService.tempJobCloningObjects.createdBy = originalJob?.custom_settings?.created_by; - mlJobService.tempJobCloningObjects.job = cloneableJob; + // use tempJobCloningData to temporarily store the job + tempJobCloningData.createdBy = originalJob?.custom_settings?.created_by; + tempJobCloningData.job = cloneableJob; if ( originalJob.data_counts.earliest_record_timestamp !== undefined && @@ -256,26 +260,28 @@ export async function cloneJob(toastNotifications, application, mlApi, mlJobServ end = originalJob.data_counts.latest_bucket_timestamp + bucketSpanMs * 2 - 1; } - mlJobService.tempJobCloningObjects.start = start; - mlJobService.tempJobCloningObjects.end = end; + tempJobCloningData.start = start; + tempJobCloningData.end = end; } } else { - // otherwise use the tempJobCloningObjects - mlJobService.tempJobCloningObjects.job = cloneableJob; + // otherwise tempJobCloningData + tempJobCloningData.job = cloneableJob; // resets the createdBy field in case it still retains previous settings - mlJobService.tempJobCloningObjects.createdBy = undefined; + tempJobCloningData.createdBy = undefined; } if (datafeed !== undefined) { - mlJobService.tempJobCloningObjects.datafeed = datafeed; + tempJobCloningData.datafeed = datafeed; } if (originalJob.calendars) { - mlJobService.tempJobCloningObjects.calendars = await mlCalendarService.fetchCalendarsByIds( + tempJobCloningData.calendars = await mlCalendarService.fetchCalendarsByIds( mlApi, originalJob.calendars ); } + jobCloningService.stashJobCloningData(tempJobCloningData); + application.navigateToApp(PLUGIN_ID, { path: ML_PAGES.ANOMALY_DETECTION_CREATE_JOB }); } catch (error) { toastNotificationServiceProvider(toastNotifications).displayErrorToast( @@ -288,9 +294,9 @@ export async function cloneJob(toastNotifications, application, mlApi, mlJobServ } } -export function closeJobs(toastNotifications, mlJobService, jobs, finish = () => {}) { +export function closeJobs(toastNotifications, mlApi, jobs, finish = () => {}) { const jobIds = jobs.map((j) => j.id); - mlJobService + mlApi.jobs .closeJobs(jobIds) .then((resp) => { showResults(toastNotifications, resp, JOB_STATE.CLOSED); @@ -309,12 +315,12 @@ export function closeJobs(toastNotifications, mlJobService, jobs, finish = () => export function resetJobs( toastNotifications, - mlJobService, + mlApi, jobIds, deleteUserAnnotations, finish = () => {} ) { - mlJobService + mlApi.jobs .resetJobs(jobIds, deleteUserAnnotations) .then((resp) => { showResults(toastNotifications, resp, JOB_ACTION.RESET); @@ -333,14 +339,14 @@ export function resetJobs( export function deleteJobs( toastNotifications, - mlJobService, + mlApi, jobs, deleteUserAnnotations, deleteAlertingRules, finish = () => {} ) { const jobIds = jobs.map((j) => j.id); - mlJobService + mlApi.jobs .deleteJobs(jobIds, deleteUserAnnotations, deleteAlertingRules) .then((resp) => { showResults(toastNotifications, resp, JOB_STATE.DELETED); @@ -449,24 +455,3 @@ function jobTagFilter(jobs, value) { .find((t) => value.some((t1) => t1 === t)); }); } -// check to see if a job has been stored in mlJobService.tempJobCloningObjects -// if it has, return an object with the minimum properties needed for the -// start datafeed modal. -export function checkForAutoStartDatafeed(mlJobService) { - const job = mlJobService.tempJobCloningObjects.job; - const datafeed = mlJobService.tempJobCloningObjects.datafeed; - if (job !== undefined) { - mlJobService.tempJobCloningObjects.job = undefined; - mlJobService.tempJobCloningObjects.datafeed = undefined; - mlJobService.tempJobCloningObjects.createdBy = undefined; - - const hasDatafeed = isPopulatedObject(datafeed); - const datafeedId = hasDatafeed ? datafeed.datafeed_id : ''; - return { - id: job.job_id, - hasDatafeed, - latestTimestampSortValue: 0, - datafeedId, - }; - } -} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/advanced_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/advanced_job_creator.ts index 04f99a6f7895bb..a2c5f2dc3438a4 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/advanced_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/advanced_job_creator.ts @@ -10,7 +10,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { Field, Aggregation, SplitField } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import { JobCreator } from './job_creator'; import type { @@ -44,13 +43,12 @@ export class AdvancedJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.ADVANCED; this._queryString = JSON.stringify(this._datafeed_config.query); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts index cb79defa163027..be78ab4e00e853 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/categorization_job_creator.ts @@ -34,7 +34,6 @@ import { DEFAULT_BUCKET_SPAN, DEFAULT_RARE_BUCKET_SPAN, } from '../../../../../../common/constants/new_job'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; @@ -66,13 +65,12 @@ export class CategorizationJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.CATEGORIZATION; this._examplesLoader = new CategorizationExamplesLoader(this, indexPattern, query); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/geo_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/geo_job_creator.ts index 986f1480e7bf30..294ea258c8299d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/geo_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/geo_job_creator.ts @@ -8,7 +8,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { Field, Aggregation, SplitField, AggFieldPair } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { JobCreator } from './job_creator'; @@ -32,13 +31,12 @@ export class GeoJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.GEO; this._wizardInitialized$.next(true); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts index 2346ff863e4893..103b789fe66b60 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator.ts @@ -22,6 +22,7 @@ import { import type { RuntimeMappings } from '@kbn/ml-runtime-field-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import { createDatafeedId } from '../../../../../../common/util/job_utils'; import type { MlApi } from '../../../../services/ml_api_service'; import type { IndexPatternTitle } from '../../../../../../common/types/kibana'; import { getQueryFromSavedSearchObject } from '../../../../util/index_utils'; @@ -36,7 +37,6 @@ import type { } from '../../../../../../common/types/anomaly_detection_jobs'; import { combineFieldsAndAggs } from '../../../../../../common/util/fields_utils'; import { createEmptyJob, createEmptyDatafeed } from './util/default_configs'; -import type { MlJobService } from '../../../../services/job_service'; import { JobRunner, type ProgressSubscriber } from '../job_runner'; import type { CREATED_BY_LABEL } from '../../../../../../common/constants/new_job'; import { JOB_TYPE, SHARED_RESULTS_INDEX_NAME } from '../../../../../../common/constants/new_job'; @@ -80,19 +80,16 @@ export class JobCreator { protected _wizardInitialized$ = new BehaviorSubject(false); public wizardInitialized$ = this._wizardInitialized$.asObservable(); public mlApi: MlApi; - public mlJobService: MlJobService; public newJobCapsService: NewJobCapsService; constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { this.mlApi = mlApi; - this.mlJobService = mlJobService; this.newJobCapsService = newJobCapsService; this._indexPattern = indexPattern; this._savedSearch = savedSearch; @@ -241,7 +238,7 @@ export class JobCreator { public set jobId(jobId: JobId) { this._job_config.job_id = jobId; this._datafeed_config.job_id = jobId; - this._datafeed_config.datafeed_id = `datafeed-${jobId}`; + this._datafeed_config.datafeed_id = createDatafeedId(jobId); if (this._useDedicatedIndex) { this._job_config.results_index_name = jobId; @@ -620,16 +617,13 @@ export class JobCreator { } } - public async createJob(): Promise { + public async createJob() { try { - const { success, resp } = await this.mlJobService.saveNewJob(this._job_config); + await this.mlApi.addJob({ + jobId: this._job_config.job_id, + job: this._job_config, + }); await this._updateCalendars(); - - if (success === true) { - return resp; - } else { - throw resp; - } } catch (error) { throw error; } @@ -638,7 +632,14 @@ export class JobCreator { public async createDatafeed(): Promise { try { const tempDatafeed = this._getDatafeedWithFilteredRuntimeMappings(); - return await this.mlJobService.saveNewDatafeed(tempDatafeed, this._job_config.job_id); + const jobId = this._job_config.job_id; + const datafeedId = createDatafeedId(jobId); + tempDatafeed.job_id = jobId; + + return this.mlApi.addDatafeed({ + datafeedId, + datafeedConfig: tempDatafeed, + }); } catch (error) { throw error; } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator_factory.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator_factory.ts index 07424b709d5b63..f4fd03a3a8bcd4 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator_factory.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/job_creator_factory.ts @@ -7,7 +7,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { SingleMetricJobCreator } from './single_metric_job_creator'; @@ -24,7 +23,6 @@ export const jobCreatorFactory = (jobType: JOB_TYPE) => ( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, @@ -57,5 +55,5 @@ export const jobCreatorFactory = jc = SingleMetricJobCreator; break; } - return new jc(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + return new jc(mlApi, newJobCapsService, indexPattern, savedSearch, query); }; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/multi_metric_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/multi_metric_job_creator.ts index cb734c84e3d23c..89a776c599455d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/multi_metric_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/multi_metric_job_creator.ts @@ -8,7 +8,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { Field, Aggregation, SplitField, AggFieldPair } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { JobCreator } from './job_creator'; @@ -31,13 +30,12 @@ export class MultiMetricJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.MULTI_METRIC; this._wizardInitialized$.next(true); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/population_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/population_job_creator.ts index 36e4e8f6f56596..bd399851b8a9a3 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/population_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/population_job_creator.ts @@ -8,7 +8,6 @@ import type { DataView } from '@kbn/data-views-plugin/public'; import type { Field, Aggregation, SplitField, AggFieldPair } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { JobCreator } from './job_creator'; @@ -30,13 +29,12 @@ export class PopulationJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.POPULATION; this._wizardInitialized$.next(true); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/rare_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/rare_job_creator.ts index e142a35ba380df..1fbbca009c2693 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/rare_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/rare_job_creator.ts @@ -13,7 +13,6 @@ import { ML_JOB_AGGREGATION, } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import type { NewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { JobCreator } from './job_creator'; @@ -38,13 +37,12 @@ export class RareJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.RARE; this._wizardInitialized$.next(true); this._rareAgg = {} as Aggregation; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/single_metric_job_creator.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/single_metric_job_creator.ts index 10a55e5210d604..a90cca5153069c 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/single_metric_job_creator.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/single_metric_job_creator.ts @@ -15,7 +15,6 @@ import { ES_AGGREGATION, } from '@kbn/ml-anomaly-utils'; import type { SavedSearch } from '@kbn/saved-search-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; import type { MlApi } from '../../../../services/ml_api_service'; import { parseInterval } from '../../../../../../common/util/parse_interval'; import { JobCreator } from './job_creator'; @@ -36,13 +35,12 @@ export class SingleMetricJobCreator extends JobCreator { constructor( mlApi: MlApi, - mlJobService: MlJobService, newJobCapsService: NewJobCapsService, indexPattern: DataView, savedSearch: SavedSearch | null, query: object ) { - super(mlApi, mlJobService, newJobCapsService, indexPattern, savedSearch, query); + super(mlApi, newJobCapsService, indexPattern, savedSearch, query); this.createdBy = CREATED_BY_LABEL.SINGLE_METRIC; this._wizardInitialized$.next(true); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts index f1cbd2dc871350..44a10b738256e8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_creator/util/general.ts @@ -20,6 +20,8 @@ import { ML_JOB_AGGREGATION, SPARSE_DATA_AGGREGATIONS, } from '@kbn/ml-anomaly-utils'; +import { cloneDeep } from 'lodash'; +import { jobCloningService } from '../../../../../services/job_cloning_service'; import type { Job, Datafeed, @@ -28,7 +30,6 @@ import type { import type { NewJobCapsService } from '../../../../../services/new_job_capabilities/new_job_capabilities_service'; import type { NavigateToPath } from '../../../../../contexts/kibana'; import { ML_PAGES } from '../../../../../../../common/constants/locator'; -import type { MlJobService } from '../../../../../services/job_service'; import type { JobCreatorType } from '..'; import { CREATED_BY_LABEL, JOB_TYPE } from '../../../../../../../common/constants/new_job'; @@ -237,53 +238,39 @@ export function isSparseDataJob(job: Job, datafeed: Datafeed): boolean { } export function convertToMultiMetricJob( - mlJobService: MlJobService, jobCreator: JobCreatorType, navigateToPath: NavigateToPath ) { jobCreator.createdBy = CREATED_BY_LABEL.MULTI_METRIC; jobCreator.modelPlot = false; - mlJobService.stashJobForCloning(jobCreator, true, true); + jobCloningService.stashJobForCloning(jobCreator, true, true); navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_MULTI_METRIC, true); } -export function convertToAdvancedJob( - mlJobService: MlJobService, - jobCreator: JobCreatorType, - navigateToPath: NavigateToPath -) { +export function convertToAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { jobCreator.createdBy = null; - mlJobService.stashJobForCloning(jobCreator, true, true); + jobCloningService.stashJobForCloning(jobCreator, true, true); navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_CONVERT_TO_ADVANCED, true); } -export function resetAdvancedJob( - mlJobService: MlJobService, - jobCreator: JobCreatorType, - navigateToPath: NavigateToPath -) { +export function resetAdvancedJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { jobCreator.createdBy = null; - mlJobService.stashJobForCloning(jobCreator, true, false); + jobCloningService.stashJobForCloning(jobCreator, true, false); navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB); } -export function resetJob( - mlJobService: MlJobService, - jobCreator: JobCreatorType, - navigateToPath: NavigateToPath -) { +export function resetJob(jobCreator: JobCreatorType, navigateToPath: NavigateToPath) { jobCreator.jobId = ''; - mlJobService.stashJobForCloning(jobCreator, true, true); + jobCloningService.stashJobForCloning(jobCreator, true, true); navigateToPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB); } export function advancedStartDatafeed( - mlJobService: MlJobService, jobCreator: JobCreatorType | null, navigateToPath: NavigateToPath ) { if (jobCreator !== null) { - mlJobService.stashJobForCloning(jobCreator, false, false); + jobCloningService.stashJobForCloning(jobCreator, false, false); } navigateToPath('/jobs'); } @@ -350,3 +337,13 @@ export function collectAggs(o: any, aggFields: Field[]) { } } } + +export function cloneDatafeed(datafeed: Datafeed) { + const tempDatafeed = cloneDeep(datafeed); + + // remove parts of the datafeed config which should not be copied + tempDatafeed.datafeed_id = ''; + tempDatafeed.job_id = ''; + + return tempDatafeed; +} diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_runner/job_runner.ts b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_runner/job_runner.ts index 335d9529297416..8c52adb23c1be8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/common/job_runner/job_runner.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/common/job_runner/job_runner.ts @@ -7,7 +7,6 @@ import { BehaviorSubject } from 'rxjs'; import type { MlApi } from '../../../../services/ml_api_service'; -import type { MlJobService } from '../../../../services/job_service'; import type { JobCreator } from '../job_creator'; import type { DatafeedId, JobId } from '../../../../../../common/types/anomaly_detection_jobs'; import { DATAFEED_STATE } from '../../../../../../common/constants/states'; @@ -23,7 +22,6 @@ export type JobAssignmentSubscriber = (assigned: boolean) => void; export class JobRunner { private _mlApi: MlApi; - private _mlJobService: MlJobService; private _jobId: JobId; private _datafeedId: DatafeedId; private _start: number = 0; @@ -45,7 +43,6 @@ export class JobRunner { constructor(jobCreator: JobCreator) { this._mlApi = jobCreator.mlApi; - this._mlJobService = jobCreator.mlJobService; this._jobId = jobCreator.jobId; this._datafeedId = jobCreator.datafeedId; this._start = jobCreator.start; @@ -72,7 +69,7 @@ export class JobRunner { private async openJob(): Promise { try { - const { node }: { node?: string } = await this._mlJobService.openJob(this._jobId); + const { node }: { node?: string } = await this._mlApi.openJob({ jobId: this._jobId }); this._jobAssignedToNode = node !== undefined && node.length > 0; this._jobAssignedToNode$.next(this._jobAssignedToNode); } catch (error) { @@ -96,12 +93,11 @@ export class JobRunner { pollProgress === true ? this._subscribers.map((s) => this._progress$.subscribe(s)) : []; await this.openJob(); - const { started } = await this._mlJobService.startDatafeed( - this._datafeedId, - this._jobId, + const { started } = await this._mlApi.startDatafeed({ + datafeedId: this._datafeedId, start, - end - ); + end, + }); this._datafeedState = DATAFEED_STATE.STARTED; this._percentageComplete = 0; diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts index 98c2a74baf5434..bb1ee523358649 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_dashboard/quick_create_job_base.ts @@ -22,7 +22,6 @@ import { FilterStateStore } from '@kbn/es-query'; import type { ErrorType } from '@kbn/ml-error-utils'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import type { Job, Datafeed } from '../../../../../common/types/anomaly_detection_jobs'; import { getFiltersForDSLQuery } from '../../../../../common/util/job_utils'; import type { CREATED_BY_LABEL } from '../../../../../common/constants/new_job'; @@ -57,8 +56,7 @@ export class QuickJobCreatorBase { protected readonly kibanaConfig: IUiSettingsClient, protected readonly timeFilter: TimefilterContract, protected readonly dashboardService: DashboardStart, - protected readonly mlApi: MlApi, - protected readonly mlJobService: MlJobService + protected readonly mlApi: MlApi ) {} protected async putJobAndDataFeed({ diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/quick_create_job.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/quick_create_job.ts index db1e025406538a..99e605fd508644 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/quick_create_job.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/quick_create_job.ts @@ -20,7 +20,6 @@ import type { LensApi } from '@kbn/lens-plugin/public'; import type { JobCreatorType } from '../common/job_creator'; import { createEmptyJob, createEmptyDatafeed } from '../common/job_creator/util/default_configs'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { CREATED_BY_LABEL, DEFAULT_BUCKET_SPAN, @@ -34,6 +33,7 @@ import { } from './utils'; import { VisualizationExtractor } from './visualization_extractor'; import { QuickJobCreatorBase, type CreateState } from '../job_from_dashboard'; +import { jobCloningService } from '../../../services/job_cloning_service'; export class QuickLensJobCreator extends QuickJobCreatorBase { constructor( @@ -42,10 +42,9 @@ export class QuickLensJobCreator extends QuickJobCreatorBase { kibanaConfig: IUiSettingsClient, timeFilter: TimefilterContract, dashboardService: DashboardStart, - mlApi: MlApi, - mlJobService: MlJobService + mlApi: MlApi ) { - super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi, mlJobService); + super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi); } public async createAndSaveJob( @@ -116,7 +115,7 @@ export class QuickLensJobCreator extends QuickJobCreatorBase { // add job config and start and end dates to the // job cloning stash, so they can be used // by the new job wizards - this.mlJobService.stashJobForCloning( + jobCloningService.stashJobForCloning( { jobConfig, datafeedConfig, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts index 0f5c102859bb87..45d54593ab2378 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_lens/route_resolver.ts @@ -15,7 +15,6 @@ import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import { QuickLensJobCreator } from './quick_create_job'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { getDefaultQuery, getRisonValue } from '../utils/new_job_utils'; @@ -26,7 +25,6 @@ interface Dependencies { timeFilter: TimefilterContract; dashboardService: DashboardStart; mlApi: MlApi; - mlJobService: MlJobService; } export async function resolver( deps: Dependencies, @@ -37,7 +35,7 @@ export async function resolver( filtersRisonString: string, layerIndexRisonString: string ) { - const { dataViews, lens, mlApi, mlJobService, timeFilter, kibanaConfig, dashboardService } = deps; + const { dataViews, lens, mlApi, timeFilter, kibanaConfig, dashboardService } = deps; if (lensSavedObjectRisonString === undefined) { throw new Error('Cannot create visualization'); } @@ -59,8 +57,7 @@ export async function resolver( kibanaConfig, timeFilter, dashboardService, - mlApi, - mlJobService + mlApi ); await jobCreator.createAndStashADJob(vis, from, to, query, filters, layerIndex); } diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/quick_create_job.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/quick_create_job.ts index cc36762162e2a3..a507eb53116af7 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/quick_create_job.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/quick_create_job.ts @@ -13,7 +13,6 @@ import type { DataView, DataViewsContract } from '@kbn/data-views-plugin/public' import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { MapApi } from '@kbn/maps-plugin/public'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { CREATED_BY_LABEL, JOB_TYPE, @@ -25,6 +24,7 @@ import { getJobsItemsFromEmbeddable } from './utils'; import type { CreateState } from '../job_from_dashboard'; import { QuickJobCreatorBase } from '../job_from_dashboard'; import { getDefaultQuery } from '../utils/new_job_utils'; +import { jobCloningService } from '../../../services/job_cloning_service'; interface VisDescriptor { dashboard: { query: Query; filters: Filter[] }; @@ -43,10 +43,9 @@ export class QuickGeoJobCreator extends QuickJobCreatorBase { kibanaConfig: IUiSettingsClient, timeFilter: TimefilterContract, dashboardService: DashboardStart, - mlApi: MlApi, - mlJobService: MlJobService + mlApi: MlApi ) { - super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi, mlJobService); + super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi); } public async createAndSaveGeoJob({ @@ -145,7 +144,7 @@ export class QuickGeoJobCreator extends QuickJobCreatorBase { // add job config and start and end dates to the // job cloning stash, so they can be used // by the new job wizards - this.mlJobService.stashJobForCloning( + jobCloningService.stashJobForCloning( { jobConfig, datafeedConfig, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/route_resolver.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/route_resolver.ts index d3a9b8641ca755..efc69e324de3d8 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/route_resolver.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_map/route_resolver.ts @@ -10,7 +10,6 @@ import type { TimefilterContract } from '@kbn/data-plugin/public'; import type { DashboardStart } from '@kbn/dashboard-plugin/public'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { QuickGeoJobCreator } from './quick_create_job'; import { getDefaultQuery, getRisonValue } from '../utils/new_job_utils'; @@ -21,7 +20,6 @@ interface Dependencies { timeFilter: TimefilterContract; dashboardService: DashboardStart; mlApi: MlApi; - mlJobService: MlJobService; } export async function resolver( deps: Dependencies, @@ -34,7 +32,7 @@ export async function resolver( toRisonString: string, layerRisonString?: string ) { - const { dataViews, kibanaConfig, timeFilter, dashboardService, mlApi, mlJobService } = deps; + const { dataViews, kibanaConfig, timeFilter, dashboardService, mlApi } = deps; const defaultLayer = { query: getDefaultQuery(), filters: [] }; const dashboard = getRisonValue(dashboardRisonString, defaultLayer); @@ -57,8 +55,7 @@ export async function resolver( kibanaConfig, timeFilter, dashboardService, - mlApi, - mlJobService + mlApi ); await jobCreator.createAndStashGeoJob( diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/quick_create_job.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/quick_create_job.ts index 92dbbced01a3c9..06272a5db0ef2d 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/quick_create_job.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/quick_create_job.ts @@ -17,9 +17,9 @@ import type { QueryDslQueryContainer } from '@elastic/elasticsearch/lib/api/type import { CREATED_BY_LABEL, DEFAULT_BUCKET_SPAN } from '../../../../../common/constants/new_job'; import { type CreateState, QuickJobCreatorBase } from '../job_from_dashboard/quick_create_job_base'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { createEmptyDatafeed, createEmptyJob } from '../common/job_creator/util/default_configs'; import type { JobCreatorType } from '../common/job_creator'; +import { jobCloningService } from '../../../services/job_cloning_service'; export const CATEGORIZATION_TYPE = { COUNT: ML_JOB_AGGREGATION.COUNT, @@ -36,10 +36,9 @@ export class QuickCategorizationJobCreator extends QuickJobCreatorBase { timeFilter: TimefilterContract, dashboardService: DashboardStart, private data: DataPublicPluginStart, - mlApi: MlApi, - mlJobService: MlJobService + mlApi: MlApi ) { - super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi, mlJobService); + super(dataViews, kibanaConfig, timeFilter, dashboardService, mlApi); } public async createAndSaveJob( @@ -119,7 +118,7 @@ export class QuickCategorizationJobCreator extends QuickJobCreatorBase { // add job config and start and end dates to the // job cloning stash, so they can be used // by the new job wizards - this.mlJobService.stashJobForCloning( + jobCloningService.stashJobForCloning( { jobConfig, datafeedConfig, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/route_resolver.ts b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/route_resolver.ts index d950c631f6bb69..1ed579a1838cc2 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/route_resolver.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/job_from_pattern_analysis/route_resolver.ts @@ -15,7 +15,6 @@ import { CATEGORIZATION_TYPE, } from './quick_create_job'; import type { MlApi } from '../../../services/ml_api_service'; -import type { MlJobService } from '../../../services/job_service'; import { getDefaultDatafeedQuery, getRisonValue } from '../utils/new_job_utils'; @@ -25,7 +24,6 @@ interface Dependencies { dashboardService: DashboardStart; data: DataPublicPluginStart; mlApi: MlApi; - mlJobService: MlJobService; } export async function resolver( deps: Dependencies, @@ -38,7 +36,7 @@ export async function resolver( toRisonString: string, queryRisonString: string ) { - const { mlApi, mlJobService, timeFilter, kibanaConfig, dashboardService, data } = deps; + const { mlApi, timeFilter, kibanaConfig, dashboardService, data } = deps; const query = getRisonValue(queryRisonString, getDefaultDatafeedQuery()); const from = getRisonValue(fromRisonString, ''); @@ -59,8 +57,7 @@ export async function resolver( timeFilter, dashboardService, data, - mlApi, - mlJobService + mlApi ); await jobCreator.createAndStashADJob( categorizationType, diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx index a45022a615be0f..8b53f9da1f3995 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/datafeed_step/components/data_view/change_data_view.tsx @@ -27,7 +27,6 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { SavedObjectFinder } from '@kbn/saved-objects-finder-plugin/public'; import { extractErrorMessage } from '@kbn/ml-error-utils'; -import { useMlJobService } from '../../../../../../../services/job_service'; import { JobCreatorContext } from '../../../job_creator_context'; import type { AdvancedJobCreator } from '../../../../../common/job_creator'; import { resetAdvancedJob } from '../../../../../common/job_creator/util/general'; @@ -63,7 +62,6 @@ export const ChangeDataViewModal: FC = ({ onClose }) => { const { jobCreator: jc } = useContext(JobCreatorContext); const jobCreator = jc as AdvancedJobCreator; - const mlJobService = useMlJobService(); const [validating, setValidating] = useState(false); const [step, setStep] = useState(STEP.PICK_DATA_VIEW); @@ -121,9 +119,8 @@ export const ChangeDataViewModal: FC = ({ onClose }) => { const applyDataView = useCallback(() => { const newIndices = newDataViewTitle.split(','); jobCreator.indices = newIndices; - resetAdvancedJob(mlJobService, jobCreator, navigateToPath); + resetAdvancedJob(jobCreator, navigateToPath); // exclude mlJobService from deps - // eslint-disable-next-line react-hooks/exhaustive-deps }, [jobCreator, newDataViewTitle, navigateToPath]); return ( diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/settings.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/settings.tsx index a68832ae3aea32..f8181709ad4086 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/settings.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/pick_fields_step/components/single_metric_view/settings.tsx @@ -10,7 +10,6 @@ import React, { Fragment, useContext } from 'react'; import { FormattedMessage } from '@kbn/i18n-react'; import { EuiFlexGroup, EuiFlexItem, EuiButtonEmpty } from '@elastic/eui'; -import { useMlJobService } from '../../../../../../../services/job_service'; import { useNavigateToPath } from '../../../../../../../contexts/kibana'; import { convertToMultiMetricJob } from '../../../../../common/job_creator/util/general'; @@ -26,11 +25,10 @@ interface Props { export const SingleMetricSettings: FC = ({ setIsValid }) => { const { jobCreator } = useContext(JobCreatorContext); - const mlJobService = useMlJobService(); const navigateToPath = useNavigateToPath(); const convertToMultiMetric = () => { - convertToMultiMetricJob(mlJobService, jobCreator, navigateToPath); + convertToMultiMetricJob(jobCreator, navigateToPath); }; return ( diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx index 446c16e50618d9..1772185dc868fe 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/components/summary_step/summary.tsx @@ -18,13 +18,13 @@ import { } from '@elastic/eui'; import { i18n } from '@kbn/i18n'; import { FormattedMessage } from '@kbn/i18n-react'; +import { createResultsUrl } from '../../../../../util/results_url'; import { useMlKibana, useNavigateToPath } from '../../../../../contexts/kibana'; import { PreviousButton } from '../wizard_nav'; import type { StepProps } from '../step_types'; import { WIZARD_STEPS } from '../step_types'; import { JobCreatorContext } from '../job_creator_context'; import type { JobRunner } from '../../../common/job_runner'; -import { useMlJobService } from '../../../../../services/job_service'; import { JsonEditorFlyout, EDITOR_MODE } from '../common/json_editor_flyout'; import { isSingleMetricJobCreator, isAdvancedJobCreator } from '../../../common/job_creator'; import { JobDetails } from './components/job_details'; @@ -49,7 +49,6 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => http: { basePath }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const navigateToPath = useNavigateToPath(); @@ -108,7 +107,7 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => try { await jobCreator.createJob(); await jobCreator.createDatafeed(); - advancedStartDatafeed(mlJobService, showStartModal ? jobCreator : null, navigateToPath); + advancedStartDatafeed(showStartModal ? jobCreator : null, navigateToPath); } catch (error) { handleJobCreationError(error); } @@ -126,7 +125,7 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => } function viewResults() { - const url = mlJobService.createResultsUrl( + const url = createResultsUrl( [jobCreator.jobId], jobCreator.start, jobCreator.end, @@ -136,11 +135,11 @@ export const SummaryStep: FC = ({ setCurrentStep, isCurrentStep }) => } function clickResetJob() { - resetJob(mlJobService, jobCreator, navigateToPath); + resetJob(jobCreator, navigateToPath); } const convertToAdvanced = () => { - convertToAdvancedJob(mlJobService, jobCreator, navigateToPath); + convertToAdvancedJob(jobCreator, navigateToPath); }; useEffect(() => { diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts index 37d39b7ca787ce..d1f9df5cbc46b6 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/index_or_search/preconfigured_job_redirect.ts @@ -7,17 +7,16 @@ import type { ApplicationStart } from '@kbn/core/public'; import type { DataViewsContract } from '@kbn/data-views-plugin/public'; -import type { MlJobService } from '../../../../services/job_service'; +import { jobCloningService } from '../../../../services/job_cloning_service'; import type { Job, Datafeed } from '../../../../../../common/types/anomaly_detection_jobs'; import { CREATED_BY_LABEL, JOB_TYPE } from '../../../../../../common/constants/new_job'; export async function preConfiguredJobRedirect( - mlJobService: MlJobService, dataViewsService: DataViewsContract, basePath: string, navigateToUrl: ApplicationStart['navigateToUrl'] ) { - const { createdBy, job, datafeed } = mlJobService.tempJobCloningObjects; + const { createdBy, job, datafeed } = jobCloningService; if (job && datafeed) { const dataViewId = await getDataViewIdFromDatafeed(job, datafeed, dataViewsService); diff --git a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx index cd90f4f552eff3..e7fe0ce66bdb28 100644 --- a/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx +++ b/x-pack/plugins/ml/public/application/jobs/new_job/pages/new_job/page.tsx @@ -13,9 +13,10 @@ import { FormattedMessage } from '@kbn/i18n-react'; import { getTimeFilterRange, useTimefilter } from '@kbn/ml-date-picker'; import { EVENT_RATE_FIELD_ID } from '@kbn/ml-anomaly-utils'; import { useTimeBuckets } from '@kbn/ml-time-buckets'; +import { jobCloningService } from '../../../../services/job_cloning_service'; import { Wizard } from './wizard'; import { WIZARD_STEPS } from '../components/step_types'; -import { getJobCreatorTitle } from '../../common/job_creator/util/general'; +import { cloneDatafeed, getJobCreatorTitle } from '../../common/job_creator/util/general'; import { jobCreatorFactory, isAdvancedJobCreator, @@ -35,7 +36,6 @@ import { JobValidator } from '../../common/job_validator'; import { useDataSource } from '../../../../contexts/ml'; import { useMlApi, useMlKibana } from '../../../../contexts/kibana'; import type { ExistingJobsAndGroups } from '../../../../services/job_service'; -import { useMlJobService } from '../../../../services/job_service'; import { useNewJobCapsService } from '../../../../services/new_job_capabilities/new_job_capabilities_service'; import { getNewJobDefaults } from '../../../../services/ml_server_info'; import { useToastNotificationService } from '../../../../services/toast_notification_service'; @@ -57,7 +57,6 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { services: { maps: mapsPlugin, uiSettings }, } = useMlKibana(); const mlApi = useMlApi(); - const mlJobService = useMlJobService(); const newJobCapsService = useNewJobCapsService(); const chartInterval = useTimeBuckets(uiSettings); @@ -66,7 +65,6 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { () => jobCreatorFactory(jobType)( mlApi, - mlJobService, newJobCapsService, dataSourceContext.selectedDataView, dataSourceContext.selectedSavedSearch, @@ -88,53 +86,36 @@ export const Page: FC = ({ existingJobsAndGroups, jobType }) => { ? WIZARD_STEPS.ADVANCED_CONFIGURE_DATAFEED : WIZARD_STEPS.TIME_RANGE; - let autoSetTimeRange = mlJobService.tempJobCloningObjects.autoSetTimeRange; - mlJobService.tempJobCloningObjects.autoSetTimeRange = false; + let autoSetTimeRange = jobCloningService.autoSetTimeRange; - if ( - mlJobService.tempJobCloningObjects.job !== undefined && - mlJobService.tempJobCloningObjects.datafeed !== undefined - ) { + if (jobCloningService.job !== undefined && jobCloningService.datafeed !== undefined) { // cloning a job - const clonedJob = mlJobService.tempJobCloningObjects.job; - const clonedDatafeed = mlJobService.cloneDatafeed(mlJobService.tempJobCloningObjects.datafeed); + const clonedJob = jobCloningService.job; + const clonedDatafeed = cloneDatafeed(jobCloningService.datafeed); initCategorizationSettings(); jobCreator.cloneFromExistingJob(clonedJob, clonedDatafeed); // if we're not skipping the time range, this is a standard job clone, so wipe the jobId - if (mlJobService.tempJobCloningObjects.skipTimeRangeStep === false) { + if (jobCloningService.skipTimeRangeStep === false) { jobCreator.jobId = ''; } else if (jobType !== JOB_TYPE.ADVANCED) { firstWizardStep = WIZARD_STEPS.PICK_FIELDS; } - mlJobService.tempJobCloningObjects.skipTimeRangeStep = false; - mlJobService.tempJobCloningObjects.job = undefined; - mlJobService.tempJobCloningObjects.datafeed = undefined; - mlJobService.tempJobCloningObjects.createdBy = undefined; - - if ( - mlJobService.tempJobCloningObjects.start !== undefined && - mlJobService.tempJobCloningObjects.end !== undefined - ) { + if (jobCloningService.start !== undefined && jobCloningService.end !== undefined) { // auto select the start and end dates for the time range picker - jobCreator.setTimeRange( - mlJobService.tempJobCloningObjects.start, - mlJobService.tempJobCloningObjects.end - ); - mlJobService.tempJobCloningObjects.start = undefined; - mlJobService.tempJobCloningObjects.end = undefined; + jobCreator.setTimeRange(jobCloningService.start, jobCloningService.end); } else { // if not start and end times are set and this is an advanced job, // auto set the time range based on the index autoSetTimeRange = autoSetTimeRange || isAdvancedJobCreator(jobCreator); } - if (mlJobService.tempJobCloningObjects.calendars) { - jobCreator.calendars = mlJobService.tempJobCloningObjects.calendars; - mlJobService.tempJobCloningObjects.calendars = undefined; + if (jobCloningService.calendars) { + jobCreator.calendars = jobCloningService.calendars; } + jobCloningService.clearJobCloningData(); } else { // creating a new job jobCreator.bucketSpan = DEFAULT_BUCKET_SPAN; diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_lens.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_lens.tsx index bc5e1f23b31bc1..74b61559d9f3ff 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_lens.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_lens.tsx @@ -15,7 +15,6 @@ import type { MlRoute, PageProps } from '../../router'; import { createPath, PageLoader } from '../../router'; import { useRouteResolver } from '../../use_resolver'; import { resolver } from '../../../jobs/new_job/job_from_lens'; -import { useMlJobService } from '../../../services/job_service'; export const fromLensRouteFactory = (): MlRoute => ({ path: createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_LENS), @@ -44,7 +43,6 @@ const PageWrapper: FC = ({ location }) => { lens, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const { context } = useRouteResolver('full', ['canCreateJob'], { redirect: () => @@ -53,7 +51,6 @@ const PageWrapper: FC = ({ location }) => { dataViews, lens, mlApi, - mlJobService, timeFilter, kibanaConfig, dashboardService, diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_map.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_map.tsx index 730dbb4cf1e263..026f219e5104f0 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_map.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_map.tsx @@ -15,7 +15,6 @@ import type { MlRoute, PageProps } from '../../router'; import { createPath, PageLoader } from '../../router'; import { useRouteResolver } from '../../use_resolver'; import { resolver } from '../../../jobs/new_job/job_from_map'; -import { useMlJobService } from '../../../services/job_service'; export const fromMapRouteFactory = (): MlRoute => ({ path: createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_MAP), @@ -50,12 +49,11 @@ const PageWrapper: FC = ({ location }) => { mlServices: { mlApi }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const { context } = useRouteResolver('full', ['canCreateJob'], { redirect: () => resolver( - { dataViews, mlApi, mlJobService, timeFilter, kibanaConfig, dashboardService }, + { dataViews, mlApi, timeFilter, kibanaConfig, dashboardService }, dashboard, dataViewId, embeddable, diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_pattern_analysis.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_pattern_analysis.tsx index d1dfcb2e21ed48..6252e54d6ae7e2 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/from_pattern_analysis.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/from_pattern_analysis.tsx @@ -15,7 +15,6 @@ import type { MlRoute, PageProps } from '../../router'; import { createPath, PageLoader } from '../../router'; import { useRouteResolver } from '../../use_resolver'; import { resolver } from '../../../jobs/new_job/job_from_pattern_analysis'; -import { useMlJobService } from '../../../services/job_service'; export const fromPatternAnalysisRouteFactory = (): MlRoute => ({ path: createPath(ML_PAGES.ANOMALY_DETECTION_CREATE_JOB_FROM_PATTERN_ANALYSIS), @@ -44,14 +43,12 @@ const PageWrapper: FC = ({ location }) => { mlServices: { mlApi }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const { context } = useRouteResolver('full', ['canCreateJob'], { redirect: () => resolver( { mlApi, - mlJobService, timeFilter: data.query.timefilter.timefilter, kibanaConfig, dashboardService, diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx index 8cc537990b1926..d08ab910fdb260 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/index_or_search.tsx @@ -21,7 +21,6 @@ import { basicResolvers } from '../../resolvers'; import { preConfiguredJobRedirect } from '../../../jobs/new_job/pages/index_or_search'; import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs'; import { NavigateToPageButton } from '../../components/navigate_to_page_button'; -import { useMlJobService } from '../../../services/job_service'; enum MODE { NEW_JOB, @@ -219,12 +218,11 @@ const PageWrapper: FC = ({ nextStepPath, mode, extraButt data: { dataViews: dataViewsService }, }, } = useMlKibana(); - const mlJobService = useMlJobService(); const newJobResolvers = { ...basicResolvers(), preConfiguredJobRedirect: () => - preConfiguredJobRedirect(mlJobService, dataViewsService, basePath.get(), navigateToUrl), + preConfiguredJobRedirect(dataViewsService, basePath.get(), navigateToUrl), }; const { context } = useRouteResolver( diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/recognize.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/recognize.tsx index fb6c2aa15b6514..035675e5ca3c1e 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/recognize.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/recognize.tsx @@ -17,11 +17,9 @@ import { useMlApi, useMlKibana, useNavigateToPath } from '../../../contexts/kiba import type { MlRoute, PageProps } from '../../router'; import { createPath, PageLoader } from '../../router'; import { useRouteResolver } from '../../use_resolver'; -import { mlJobServiceFactory } from '../../../services/job_service'; import { getBreadcrumbWithUrlForApp } from '../../breadcrumbs'; import { useCreateADLinks } from '../../../components/custom_hooks/use_create_ad_links'; import { DataSourceContextProvider } from '../../../contexts/ml'; -import { useToastNotificationService } from '../../../services/toast_notification_service'; const Page = dynamic(async () => ({ default: (await import('../../../jobs/new_job/recognize')).Page, @@ -56,12 +54,10 @@ export const checkViewOrCreateRouteFactory = (): MlRoute => ({ const PageWrapper: FC = ({ location }) => { const { id } = parse(location.search, { sort: false }); const mlApi = useMlApi(); - const toastNotificationService = useToastNotificationService(); const { context, results } = useRouteResolver('full', ['canGetJobs'], { ...basicResolvers(), - existingJobsAndGroups: () => - mlJobServiceFactory(toastNotificationService, mlApi).getJobAndGroupIds(), + existingJobsAndGroups: () => mlApi.jobs.getAllJobAndGroupIds(), }); return ( diff --git a/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx b/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx index d5f43fab97f452..248dcd6d84771b 100644 --- a/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx +++ b/x-pack/plugins/ml/public/application/routing/routes/new_job/wizard.tsx @@ -19,8 +19,6 @@ import type { MlRoute, PageProps } from '../../router'; import { createPath, PageLoader } from '../../router'; import { useRouteResolver } from '../../use_resolver'; import { JOB_TYPE } from '../../../../../common/constants/new_job'; -import { mlJobServiceFactory } from '../../../services/job_service'; -import { useToastNotificationService } from '../../../services/toast_notification_service'; import { loadNewJobCapabilities, ANOMALY_DETECTOR, @@ -210,8 +208,6 @@ const PageWrapper: FC = ({ location, jobType }) => { mlServices: { mlApi }, }, } = useMlKibana(); - const toastNotificationService = useToastNotificationService(); - const { context, results } = useRouteResolver('full', ['canGetJobs', 'canCreateJob'], { ...basicResolvers(), // TODO useRouteResolver should be responsible for the redirect @@ -225,8 +221,7 @@ const PageWrapper: FC = ({ location, jobType }) => { savedSearchService, ANOMALY_DETECTOR ), - existingJobsAndGroups: () => - mlJobServiceFactory(toastNotificationService, mlApi).getJobAndGroupIds(), + existingJobsAndGroups: () => mlApi.jobs.getAllJobAndGroupIds(), }); return ( diff --git a/x-pack/plugins/ml/public/application/services/job_cloning_service.ts b/x-pack/plugins/ml/public/application/services/job_cloning_service.ts new file mode 100644 index 00000000000000..1981640c415cd0 --- /dev/null +++ b/x-pack/plugins/ml/public/application/services/job_cloning_service.ts @@ -0,0 +1,130 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { isPopulatedObject } from '@kbn/ml-is-populated-object'; +import type { JobCreator } from '../jobs/new_job/common/job_creator/job_creator'; + +interface TempJobCloningData { + createdBy: any; + datafeed: any; + job: any; + skipTimeRangeStep: boolean; + start?: any; + end?: any; + calendars: any; + autoSetTimeRange?: boolean; +} + +export class JobCloningService { + // tempJobCloningData -> used to pass a job object between the job management page and + // and the advanced wizard. + // if populated when loading the advanced wizard, the job is used for cloning. + // if populated when loading the job management page, the start datafeed modal + // is automatically opened. + private tempJobCloningData: TempJobCloningData = { + createdBy: undefined, + datafeed: undefined, + job: undefined, + skipTimeRangeStep: false, + start: undefined, + end: undefined, + calendars: undefined, + autoSetTimeRange: false, + }; + + public getJobCloningData(): Readonly { + return this.tempJobCloningData; + } + + clearJobCloningData() { + this.tempJobCloningData = { + createdBy: undefined, + datafeed: undefined, + job: undefined, + skipTimeRangeStep: false, + start: undefined, + end: undefined, + calendars: undefined, + autoSetTimeRange: false, + }; + } + + stashJobForCloning( + jobCreator: JobCreator, + skipTimeRangeStep: boolean, + includeTimeRange: boolean, + autoSetTimeRange: boolean = false + ) { + const tempJobCloningData: TempJobCloningData = { + job: jobCreator.jobConfig, + datafeed: jobCreator.datafeedConfig, + createdBy: jobCreator.createdBy ?? undefined, + // skip over the time picker step of the wizard + skipTimeRangeStep, + calendars: jobCreator.calendars, + ...(includeTimeRange === true && autoSetTimeRange === false + ? // auto select the start and end dates of the time picker + { + start: jobCreator.start, + end: jobCreator.end, + } + : { autoSetTimeRange: true }), + }; + + this.tempJobCloningData = tempJobCloningData; + } + + public checkForAutoStartDatafeed() { + const job = this.tempJobCloningData.job; + const datafeed = this.tempJobCloningData.datafeed; + if (job !== undefined) { + this.tempJobCloningData.job = undefined; + this.tempJobCloningData.datafeed = undefined; + this.tempJobCloningData.createdBy = undefined; + + const hasDatafeed = isPopulatedObject(datafeed); + const datafeedId = hasDatafeed ? datafeed.datafeed_id : ''; + return { + id: job.job_id, + hasDatafeed, + latestTimestampSortValue: 0, + datafeedId, + }; + } + } + + public stashJobCloningData(config: TempJobCloningData) { + this.tempJobCloningData = config; + } + + public get createdBy() { + return this.tempJobCloningData.createdBy; + } + public get datafeed() { + return this.tempJobCloningData.datafeed; + } + public get job() { + return this.tempJobCloningData.job; + } + public get skipTimeRangeStep() { + return this.tempJobCloningData.skipTimeRangeStep; + } + public get start() { + return this.tempJobCloningData.start; + } + public get end() { + return this.tempJobCloningData.end; + } + public get calendars() { + return this.tempJobCloningData.calendars; + } + public get autoSetTimeRange() { + return this.tempJobCloningData.autoSetTimeRange; + } +} + +export const jobCloningService = new JobCloningService(); diff --git a/x-pack/plugins/ml/public/application/services/job_service.d.ts b/x-pack/plugins/ml/public/application/services/job_service.d.ts index 54a1fb3170918a..8827b0b335ff4e 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.d.ts +++ b/x-pack/plugins/ml/public/application/services/job_service.d.ts @@ -5,12 +5,8 @@ * 2.0. */ -import type { TimeRange } from '@kbn/data-plugin/common/query/timefilter/types'; -import type { CombinedJob, Datafeed, Job } from '../../../common/types/anomaly_detection_jobs'; -import type { Calendar } from '../../../common/types/calendars'; -import type { ToastNotificationService } from './toast_notification_service'; +import type { CombinedJob } from '../../../common/types/anomaly_detection_jobs'; import type { MlApi } from './ml_api_service'; -import type { JobCreatorType } from '../jobs/new_job/common/job_creator'; export interface ExistingJobsAndGroups { jobIds: string[]; @@ -19,50 +15,13 @@ export interface ExistingJobsAndGroups { export declare interface MlJobService { jobs: CombinedJob[]; - createResultsUrlForJobs: (jobs: any[], target: string, timeRange?: TimeRange) => string; - tempJobCloningObjects: { - createdBy?: string; - datafeed?: Datafeed; - job?: Job; - skipTimeRangeStep: boolean; - start?: number; - end?: number; - calendars: Calendar[] | undefined; - autoSetTimeRange?: boolean; - }; skipTimeRangeStep: boolean; - saveNewJob(job: Job): Promise; - cloneDatafeed(Datafeed: Datafeed): Datafeed; - openJob(jobId: string): Promise; - saveNewDatafeed(datafeedConfig: any, jobId: string): Promise; - startDatafeed( - datafeedId: string, - jobId: string, - start: number | undefined, - end: number | undefined - ): Promise; - forceStartDatafeeds( - dIds: string[], - start: number | undefined, - end: number | undefined - ): Promise; - createResultsUrl(jobId: string[], start: number, end: number, location: string): string; - getJobAndGroupIds(): Promise; getJob(jobId: string): CombinedJob; loadJobsWrapper(): Promise; customUrlsByJob: Record; detectorsByJob: Record; - stashJobForCloning( - jobCreator: JobCreatorType, - skipTimeRangeStep: boolean = false, - includeTimeRange: boolean = false, - autoSetTimeRange: boolean = false - ): void; } -export const mlJobServiceFactory: ( - toastNotificationService: ToastNotificationService, - mlApi: MlApi -) => MlJobService; +export const mlJobServiceFactory: (mlApi: MlApi) => MlJobService; export const useMlJobService: () => MlJobService; 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 dc7d96f4e32361..936317675fbbb9 100644 --- a/x-pack/plugins/ml/public/application/services/job_service.js +++ b/x-pack/plugins/ml/public/application/services/job_service.js @@ -5,42 +5,21 @@ * 2.0. */ -import { cloneDeep, each, find, get, isNumber } from 'lodash'; -import moment from 'moment'; - -import { validateTimeRange, TIME_FORMAT } from '@kbn/ml-date-utils'; +import { cloneDeep, each, find, get } from 'lodash'; import { parseInterval } from '../../../common/util/parse_interval'; +import { createDatafeedId } from '../../../common/util/job_utils'; import { isWebUrl } from '../util/url_utils'; import { useMlApi } from '../contexts/kibana'; -import { useToastNotificationService } from './toast_notification_service'; - let jobs = []; let datafeedIds = {}; class JobService { - constructor(toastNotificationService, ml) { - this.toastNotificationService = toastNotificationService; + constructor(ml) { this.ml = ml; - // tempJobCloningObjects -> used to pass a job object between the job management page and - // and the advanced wizard. - // if populated when loading the advanced wizard, the job is used for cloning. - // if populated when loading the job management page, the start datafeed modal - // is automatically opened. - this.tempJobCloningObjects = { - createdBy: undefined, - datafeed: undefined, - job: undefined, - skipTimeRangeStep: false, - start: undefined, - end: undefined, - calendars: undefined, - autoSetTimeRange: false, - }; - this.jobs = []; // Provide ready access to widely used basic job properties. @@ -112,7 +91,6 @@ class JobService { function error(err) { console.log('jobService error getting list of jobs:', err); - this.toastNotificationService.displayErrorToast(err); reject({ jobs, err }); } }); @@ -168,7 +146,7 @@ class JobService { } } - const datafeedId = this.getDatafeedId(jobId); + const datafeedId = createDatafeedId(jobId); this.loadDatafeeds(datafeedId).then((datafeedsResp) => { for (let i = 0; i < jobs.length; i++) { @@ -195,7 +173,6 @@ class JobService { function error(err) { console.log('JobService error getting list of jobs:', err); - this.toastNotificationService.displayErrorToast(err); reject({ jobs, err }); } }); @@ -235,56 +212,11 @@ class JobService { function error(err) { console.log('loadDatafeeds error getting list of datafeeds:', err); - this.toastNotificationService.displayErrorToast(err); reject({ jobs, err }); } }); } - updateSingleJobDatafeedState(jobId) { - return new Promise((resolve, reject) => { - const datafeedId = this.getDatafeedId(jobId); - - this.ml - .getDatafeedStats({ datafeedId }) - .then((resp) => { - // console.log('updateSingleJobCounts controller query response:', resp); - const datafeeds = resp.datafeeds; - let state = 'UNKNOWN'; - if (datafeeds && datafeeds.length) { - state = datafeeds[0].state; - } - resolve(state); - }) - .catch((resp) => { - reject(resp); - }); - }); - } - - saveNewJob(job) { - // run then and catch through the same check - function func(resp) { - console.log('Response for job query:', resp); - const success = checkSaveResponse(resp, job); - return { success, job, resp }; - } - - // return the promise chain - return this.ml.addJob({ jobId: job.job_id, job }).then(func).catch(func); - } - - cloneDatafeed(datafeed) { - const tempDatafeed = cloneDeep(datafeed); - - // remove parts of the datafeed config which should not be copied - if (tempDatafeed) { - delete tempDatafeed.datafeed_id; - delete tempDatafeed.job_id; - } - return tempDatafeed; - } - // find a job based on the id getJob(jobId) { const job = find(jobs, (j) => { @@ -293,175 +225,6 @@ class JobService { return job; } - - openJob(jobId) { - return this.ml.openJob({ jobId }); - } - - closeJob(jobId) { - return this.ml.closeJob({ jobId }); - } - - saveNewDatafeed(datafeedConfig, jobId) { - const datafeedId = `datafeed-${jobId}`; - datafeedConfig.job_id = jobId; - - return this.ml.addDatafeed({ - datafeedId, - datafeedConfig, - }); - } - - // start the datafeed for a given job - // refresh the job state on start success - startDatafeed(datafeedId, jobId, start, end) { - return new Promise((resolve, reject) => { - // if the end timestamp is a number, add one ms to it to make it - // inclusive of the end of the data - if (isNumber(end)) { - end++; - } - - this.ml - .startDatafeed({ - datafeedId, - start, - end, - }) - .then((resp) => { - resolve(resp); - }) - .catch((err) => { - console.log('jobService error starting datafeed:', err); - reject(err); - }); - }); - } - - forceStartDatafeeds(dIds, start, end) { - return this.ml.jobs.forceStartDatafeeds(dIds, start, end); - } - - stopDatafeeds(dIds) { - return this.ml.jobs.stopDatafeeds(dIds); - } - - deleteJobs(jIds, deleteUserAnnotations, deleteAlertingRules) { - return this.ml.jobs.deleteJobs(jIds, deleteUserAnnotations, deleteAlertingRules); - } - - closeJobs(jIds) { - return this.ml.jobs.closeJobs(jIds); - } - - resetJobs(jIds, deleteUserAnnotations) { - return this.ml.jobs.resetJobs(jIds, deleteUserAnnotations); - } - - validateDetector(detector) { - return new Promise((resolve, reject) => { - if (detector) { - this.ml - .validateDetector({ detector }) - .then((resp) => { - resolve(resp); - }) - .catch((resp) => { - reject(resp); - }); - } else { - reject({}); - } - }); - } - - getDatafeedId(jobId) { - let datafeedId = datafeedIds[jobId]; - if (datafeedId === undefined) { - datafeedId = `datafeed-${jobId}`; - } - return datafeedId; - } - - // get the list of job group ids as well as how many jobs are in each group - getJobGroups() { - const groups = []; - const tempGroups = {}; - this.jobs.forEach((job) => { - if (Array.isArray(job.groups)) { - job.groups.forEach((group) => { - if (tempGroups[group] === undefined) { - tempGroups[group] = [job]; - } else { - tempGroups[group].push(job); - } - }); - } - }); - each(tempGroups, (js, id) => { - groups.push({ id, jobs: js }); - }); - return groups; - } - - createResultsUrlForJobs(jobsList, resultsPage, timeRange) { - return createResultsUrlForJobs(jobsList, resultsPage, timeRange); - } - - createResultsUrl(jobIds, from, to, resultsPage) { - return createResultsUrl(jobIds, from, to, resultsPage); - } - - async getJobAndGroupIds() { - try { - return await this.ml.jobs.getAllJobAndGroupIds(); - } catch (error) { - return { - jobIds: [], - groupIds: [], - }; - } - } - - stashJobForCloning(jobCreator, skipTimeRangeStep, includeTimeRange, autoSetTimeRange) { - const tempJobCloningObjects = { - job: jobCreator.jobConfig, - datafeed: jobCreator.datafeedConfig, - createdBy: jobCreator.createdBy ?? undefined, - // skip over the time picker step of the wizard - skipTimeRangeStep, - calendars: jobCreator.calendars, - ...(includeTimeRange === true && autoSetTimeRange === false - ? // auto select the start and end dates of the time picker - { - start: jobCreator.start, - end: jobCreator.end, - } - : { autoSetTimeRange: true }), - }; - - this.tempJobCloningObjects = tempJobCloningObjects; - } -} - -// private function used to check the job saving response -function checkSaveResponse(resp, origJob) { - if (resp) { - if (resp.job_id) { - if (resp.job_id === origJob.job_id) { - console.log('checkSaveResponse(): save successful'); - return true; - } - } else { - if (resp.errorCode) { - console.log('checkSaveResponse(): save failed', resp); - return false; - } - } - } else { - console.log('checkSaveResponse(): response is empty'); - return false; - } } function processBasicJobInfo(localJobService, jobsList) { @@ -521,90 +284,16 @@ function processBasicJobInfo(localJobService, jobsList) { return processedJobsList; } -function createResultsUrlForJobs(jobsList, resultsPage, userTimeRange) { - let from = undefined; - let to = undefined; - let mode = 'absolute'; - const jobIds = jobsList.map((j) => j.id); - - // if the custom default time filter is set and enabled in advanced settings - // if time is either absolute date or proper datemath format - if (validateTimeRange(userTimeRange)) { - from = userTimeRange.from; - to = userTimeRange.to; - // if both pass datemath's checks but are not technically absolute dates, use 'quick' - // e.g. "now-15m" "now+1d" - const fromFieldAValidDate = moment(userTimeRange.from).isValid(); - const toFieldAValidDate = moment(userTimeRange.to).isValid(); - if (!fromFieldAValidDate && !toFieldAValidDate) { - return createResultsUrl(jobIds, from, to, resultsPage, 'quick'); - } - } else { - // if time range is specified but with incorrect format - // change back to the default time range but alert the user - // that the advanced setting config is invalid - if (userTimeRange) { - mode = 'invalid'; - } - - if (jobsList.length === 1) { - from = jobsList[0].earliestTimestampMs; - to = jobsList[0].latestResultsTimestampMs; // Will be max(latest source data, latest bucket results) - } else { - const jobsWithData = jobsList.filter((j) => j.earliestTimestampMs !== undefined); - if (jobsWithData.length > 0) { - from = Math.min(...jobsWithData.map((j) => j.earliestTimestampMs)); - to = Math.max(...jobsWithData.map((j) => j.latestResultsTimestampMs)); - } - } - } - - const fromString = moment(from).format(TIME_FORMAT); // Defaults to 'now' if 'from' is undefined - const toString = moment(to).format(TIME_FORMAT); // Defaults to 'now' if 'to' is undefined - - return createResultsUrl(jobIds, fromString, toString, resultsPage, mode); -} - -function createResultsUrl(jobIds, start, end, resultsPage, mode = 'absolute') { - const idString = jobIds.map((j) => `'${j}'`).join(','); - let from; - let to; - let path = ''; - - if (resultsPage !== undefined) { - path += resultsPage; - } - - if (mode === 'quick') { - from = start; - to = end; - } else { - from = moment(start).toISOString(); - to = moment(end).toISOString(); - } - - path += `?_g=(ml:(jobIds:!(${idString}))`; - path += `,refreshInterval:(display:Off,pause:!t,value:0),time:(from:'${from}'`; - path += `,to:'${to}'`; - if (mode === 'invalid') { - path += `,mode:invalid`; - } - path += "))&_a=(query:(query_string:(analyze_wildcard:!t,query:'*')))"; - - return path; -} - // This is to retain the singleton behavior of the previous direct instantiation and export. let mlJobService; -export const mlJobServiceFactory = (toastNotificationService, mlApi) => { +export const mlJobServiceFactory = (mlApi) => { if (mlJobService) return mlJobService; - mlJobService = new JobService(toastNotificationService, mlApi); + mlJobService = new JobService(mlApi); return mlJobService; }; export const useMlJobService = () => { - const toastNotificationService = useToastNotificationService(); const mlApi = useMlApi(); - return mlJobServiceFactory(toastNotificationService, mlApi); + return mlJobServiceFactory(mlApi); }; diff --git a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts index f185a6452d654b..4939149b96417e 100644 --- a/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts +++ b/x-pack/plugins/ml/public/application/services/ml_api_service/index.ts @@ -11,6 +11,7 @@ import type * as estypes from '@elastic/elasticsearch/lib/api/typesWithBodyKey'; import type { RuntimeMappings } from '@kbn/ml-runtime-field-utils'; +import { isNumber } from 'lodash'; import { ML_INTERNAL_BASE_PATH } from '../../../../common/constants/app'; import type { MlServerDefaults, @@ -299,6 +300,12 @@ export function mlApiProvider(httpService: HttpService) { start?: number; end?: number; }) { + // if the end timestamp is a number, add one ms to it to make it + // inclusive of the end of the data + if (isNumber(end)) { + end++; + } + const body = JSON.stringify({ ...(start !== undefined ? { start } : {}), ...(end !== undefined ? { end } : {}), diff --git a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js index c4951b9041fc1a..601bbf058868fd 100644 --- a/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js +++ b/x-pack/plugins/ml/public/application/timeseriesexplorer/timeseriesexplorer.js @@ -160,7 +160,7 @@ export class TimeSeriesExplorer extends React.Component { this.mlApi = constructorContext.services.mlServices.mlApi; this.mlForecastService = forecastServiceFactory(this.mlApi); this.mlIndexUtils = indexServiceFactory(this.dataViewsService); - this.mlJobService = mlJobServiceFactory(this.toastNotificationService, this.mlApi); + this.mlJobService = mlJobServiceFactory(this.mlApi); this.mlResultsService = mlResultsServiceProvider(this.mlApi); this.mlTimeSeriesExplorer = timeSeriesExplorerServiceFactory( constructorContext.services.uiSettings, diff --git a/x-pack/plugins/ml/public/application/util/get_services.ts b/x-pack/plugins/ml/public/application/util/get_services.ts index 031504509dff79..37a6c28f7cbd39 100644 --- a/x-pack/plugins/ml/public/application/util/get_services.ts +++ b/x-pack/plugins/ml/public/application/util/get_services.ts @@ -17,7 +17,6 @@ import { HttpService } from '../services/http_service'; import { mlApiProvider } from '../services/ml_api_service'; import { mlUsageCollectionProvider } from '../services/usage_collection'; import { mlJobServiceFactory } from '../services/job_service'; -import { toastNotificationServiceProvider } from '../services/toast_notification_service'; import { indexServiceFactory } from './index_service'; /** @@ -30,8 +29,7 @@ export function getMlGlobalServices( ) { const httpService = new HttpService(coreStart.http); const mlApi = mlApiProvider(httpService); - const toastNotificationService = toastNotificationServiceProvider(coreStart.notifications.toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); + const mlJobService = mlJobServiceFactory(mlApi); // Note on the following services: // - `mlIndexUtils` is just instantiated here to be passed on to `mlFieldFormatService`, // but it's not being made available as part of global services. Since it's just diff --git a/x-pack/plugins/ml/public/application/util/results_url.ts b/x-pack/plugins/ml/public/application/util/results_url.ts new file mode 100644 index 00000000000000..77939f665b60fd --- /dev/null +++ b/x-pack/plugins/ml/public/application/util/results_url.ts @@ -0,0 +1,95 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License + * 2.0; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import moment from 'moment'; + +import { validateTimeRange, TIME_FORMAT } from '@kbn/ml-date-utils'; +import type { TimeRange } from '@kbn/es-query'; +import type { MlSummaryJob } from '../../../common'; + +export function createResultsUrlForJobs( + jobsList: MlSummaryJob[], + resultsPage: string, + userTimeRange: TimeRange +) { + let from; + let to; + let mode = 'absolute'; + const jobIds = jobsList.map((j) => j.id); + + // if the custom default time filter is set and enabled in advanced settings + // if time is either absolute date or proper datemath format + if (validateTimeRange(userTimeRange)) { + from = userTimeRange.from; + to = userTimeRange.to; + // if both pass datemath's checks but are not technically absolute dates, use 'quick' + // e.g. "now-15m" "now+1d" + const fromFieldAValidDate = moment(userTimeRange.from).isValid(); + const toFieldAValidDate = moment(userTimeRange.to).isValid(); + if (!fromFieldAValidDate && !toFieldAValidDate) { + return createResultsUrl(jobIds, from, to, resultsPage, 'quick'); + } + } else { + // if time range is specified but with incorrect format + // change back to the default time range but alert the user + // that the advanced setting config is invalid + if (userTimeRange) { + mode = 'invalid'; + } + + if (jobsList.length === 1) { + from = jobsList[0].earliestTimestampMs; + to = jobsList[0].latestResultsTimestampMs; // Will be max(latest source data, latest bucket results) + } else { + const jobsWithData = jobsList.filter((j) => j.earliestTimestampMs !== undefined); + if (jobsWithData.length > 0) { + from = Math.min(...jobsWithData.map((j) => j.earliestTimestampMs!)); + to = Math.max(...jobsWithData.map((j) => j.latestResultsTimestampMs!)); + } + } + } + + const fromString = moment(from).format(TIME_FORMAT); // Defaults to 'now' if 'from' is undefined + const toString = moment(to).format(TIME_FORMAT); // Defaults to 'now' if 'to' is undefined + + return createResultsUrl(jobIds, fromString, toString, resultsPage, mode); +} + +export function createResultsUrl( + jobIds: string[], + start: number | string, + end: number | string, + resultsPage: string, + mode = 'absolute' +) { + const idString = jobIds.map((j) => `'${j}'`).join(','); + let from; + let to; + let path = ''; + + if (resultsPage !== undefined) { + path += resultsPage; + } + + if (mode === 'quick') { + from = start; + to = end; + } else { + from = moment(start).toISOString(); + to = moment(end).toISOString(); + } + + path += `?_g=(ml:(jobIds:!(${idString}))`; + path += `,refreshInterval:(display:Off,pause:!t,value:0),time:(from:'${from}'`; + path += `,to:'${to}'`; + if (mode === 'invalid') { + path += `,mode:invalid`; + } + path += "))&_a=(query:(query_string:(analyze_wildcard:!t,query:'*')))"; + + return path; +} diff --git a/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts index f5270ab01ef9f8..078db5b83f5068 100644 --- a/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts +++ b/x-pack/plugins/ml/public/embeddables/anomaly_charts/get_anomaly_charts_services_dependencies.ts @@ -22,7 +22,6 @@ export const getAnomalyChartsServiceDependencies = async ( { mlApiProvider }, { mlJobServiceFactory }, { mlResultsServiceProvider }, - { toastNotificationServiceProvider }, ] = await Promise.all([ await import('../../application/services/anomaly_detector_service'), await import('../../application/services/field_format_service_factory'), @@ -30,13 +29,11 @@ export const getAnomalyChartsServiceDependencies = async ( await import('../../application/services/ml_api_service'), await import('../../application/services/job_service'), await import('../../application/services/results_service'), - await import('../../application/services/toast_notification_service'), ]); const httpService = new HttpService(coreStart.http); const anomalyDetectorService = new AnomalyDetectorService(httpService); const mlApi = mlApiProvider(httpService); - const toastNotificationService = toastNotificationServiceProvider(coreStart.notifications.toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); + const mlJobService = mlJobServiceFactory(mlApi); const mlResultsService = mlResultsServiceProvider(mlApi); const anomalyExplorerService = new AnomalyExplorerChartsService( pluginsStart.data.query.timefilter.timefilter, diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/aiops/flyout/create_job.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/aiops/flyout/create_job.tsx index e4a3e0d05cbde3..5d5193566bfbb0 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/aiops/flyout/create_job.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/aiops/flyout/create_job.tsx @@ -34,8 +34,6 @@ import { } from '../../../../application/jobs/new_job/job_from_pattern_analysis'; import { useMlFromLensKibanaContext } from '../../common/context'; import { JobDetails, type CreateADJobParams } from '../../common/job_details'; -import { mlJobServiceFactory } from '../../../../application/services/job_service'; -import { toastNotificationServiceProvider } from '../../../../application/services/toast_notification_service'; interface Props { dataView: DataView; @@ -51,13 +49,9 @@ export const CreateJob: FC = ({ dataView, field, query, timeRange }) => { share, uiSettings, dashboardService, - notifications: { toasts }, mlServices: { mlApi }, }, } = useMlFromLensKibanaContext(); - const toastNotificationService = toastNotificationServiceProvider(toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); - const [categorizationType, setCategorizationType] = useState( CATEGORIZATION_TYPE.COUNT ); @@ -96,10 +90,9 @@ export const CreateJob: FC = ({ dataView, field, query, timeRange }) => { data.query.timefilter.timefilter, dashboardService, data, - mlApi, - mlJobService + mlApi ), - [dashboardService, data, mlApi, mlJobService, uiSettings] + [dashboardService, data, mlApi, uiSettings] ); function createADJobInWizard() { diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/compatible_layer.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/compatible_layer.tsx index 967a9dbb843049..463f432db28b5f 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/compatible_layer.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/lens/lens_vis_layer_selection_flyout/layer/compatible_layer.tsx @@ -19,8 +19,6 @@ import { JOB_TYPE } from '../../../../../../common/constants/new_job'; import { useMlFromLensKibanaContext } from '../../../common/context'; import type { CreateADJobParams } from '../../../common/job_details'; import { JobDetails } from '../../../common/job_details'; -import { mlJobServiceFactory } from '../../../../../application/services/job_service'; -import { toastNotificationServiceProvider } from '../../../../../application/services/toast_notification_service'; interface Props { layer: LayerResult; @@ -36,12 +34,9 @@ export const CompatibleLayer: FC = ({ layer, layerIndex, embeddable }) => uiSettings, lens, dashboardService, - notifications: { toasts }, mlServices: { mlApi }, }, } = useMlFromLensKibanaContext(); - const toastNotificationService = toastNotificationServiceProvider(toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); const quickJobCreator = useMemo( () => @@ -51,8 +46,7 @@ export const CompatibleLayer: FC = ({ layer, layerIndex, embeddable }) => uiSettings, data.query.timefilter.timefilter, dashboardService, - mlApi, - mlJobService + mlApi ), // eslint-disable-next-line react-hooks/exhaustive-deps [data, uiSettings] diff --git a/x-pack/plugins/ml/public/embeddables/job_creation/map/map_vis_layer_selection_flyout/layer/compatible_layer.tsx b/x-pack/plugins/ml/public/embeddables/job_creation/map/map_vis_layer_selection_flyout/layer/compatible_layer.tsx index c91785e91573a1..8847d21b2f4e33 100644 --- a/x-pack/plugins/ml/public/embeddables/job_creation/map/map_vis_layer_selection_flyout/layer/compatible_layer.tsx +++ b/x-pack/plugins/ml/public/embeddables/job_creation/map/map_vis_layer_selection_flyout/layer/compatible_layer.tsx @@ -29,8 +29,6 @@ import { import { useMlFromLensKibanaContext } from '../../../common/context'; import type { CreateADJobParams } from '../../../common/job_details'; import { JobDetails } from '../../../common/job_details'; -import { mlJobServiceFactory } from '../../../../../application/services/job_service'; -import { toastNotificationServiceProvider } from '../../../../../application/services/toast_notification_service'; interface DropDownLabel { label: string; @@ -53,12 +51,9 @@ export const CompatibleLayer: FC = ({ embeddable, layer, layerIndex }) => share, uiSettings, dashboardService, - notifications: { toasts }, mlServices: { mlApi }, }, } = useMlFromLensKibanaContext(); - const toastNotificationService = toastNotificationServiceProvider(toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); const quickJobCreator = useMemo( () => @@ -67,8 +62,7 @@ export const CompatibleLayer: FC = ({ embeddable, layer, layerIndex }) => uiSettings, data.query.timefilter.timefilter, dashboardService, - mlApi, - mlJobService + mlApi ), // eslint-disable-next-line react-hooks/exhaustive-deps [data, uiSettings] diff --git a/x-pack/plugins/ml/public/embeddables/single_metric_viewer/get_services.ts b/x-pack/plugins/ml/public/embeddables/single_metric_viewer/get_services.ts index 180b29dbd0d995..62afd5e251b6af 100644 --- a/x-pack/plugins/ml/public/embeddables/single_metric_viewer/get_services.ts +++ b/x-pack/plugins/ml/public/embeddables/single_metric_viewer/get_services.ts @@ -50,7 +50,7 @@ export const getMlServices = async ( const anomalyDetectorService = new AnomalyDetectorService(httpService); const mlApi = mlApiProvider(httpService); const toastNotificationService = toastNotificationServiceProvider(coreStart.notifications.toasts); - const mlJobService = mlJobServiceFactory(toastNotificationService, mlApi); + const mlJobService = mlJobServiceFactory(mlApi); const mlResultsService = mlResultsServiceProvider(mlApi); const mlTimeSeriesSearchService = timeSeriesSearchServiceFactory(mlResultsService, mlApi); const mlTimeSeriesExplorerService = timeSeriesExplorerServiceFactory( 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 7e29fe89661517..172d5d1b7e5829 100644 --- a/x-pack/plugins/ml/server/models/job_service/jobs.ts +++ b/x-pack/plugins/ml/server/models/job_service/jobs.ts @@ -14,6 +14,7 @@ import { getSingleMetricViewerJobErrorMessage, parseTimeIntervalForJob, isJobWithGeoData, + createDatafeedId, } from '../../../common/util/job_utils'; import { JOB_STATE, DATAFEED_STATE } from '../../../common/constants/states'; import type { JobAction } from '../../../common/constants/job_actions'; @@ -626,7 +627,7 @@ export function jobsProvider( } async function getLookBackProgress(jobId: string, start: number, end: number) { - const datafeedId = `datafeed-${jobId}`; + const datafeedId = createDatafeedId(jobId); const [body, isRunning] = await Promise.all([ mlClient.getJobStats({ job_id: jobId }), isDatafeedRunning(datafeedId),