From 45fdfbfa99eacec02edfce9630bfb85ba6ac427c Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 7 Nov 2018 12:45:25 +0100 Subject: [PATCH 1/3] [ML] Restore missing job timepicker modal. --- .../job_timepicker_modal/datafeed_service.js | 32 ++++ .../components/job_timepicker_modal/index.js | 12 ++ .../job_timepicker_modal.html | 109 ++++++++++++++ .../job_timepicker_modal_controller.js | 142 ++++++++++++++++++ .../job_timepicker_modal/styles/main.less | 85 +++++++++++ .../ml/public/jobs/new_job/advanced/index.js | 1 + 6 files changed, 381 insertions(+) create mode 100644 x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/datafeed_service.js create mode 100644 x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/index.js create mode 100644 x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal.html create mode 100644 x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal_controller.js create mode 100644 x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/styles/main.less diff --git a/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/datafeed_service.js b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/datafeed_service.js new file mode 100644 index 00000000000000..565c61ce6957e1 --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/datafeed_service.js @@ -0,0 +1,32 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + +import jobTimePickerTemplate from './job_timepicker_modal.html'; + +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/ml'); + +module.service('mlDatafeedService', function ($modal) { + + this.openJobTimepickerWindow = function (job) { + $modal.open({ + template: jobTimePickerTemplate, + controller: 'MlJobTimepickerModal', + backdrop: 'static', + keyboard: false, + resolve: { + params: function () { + return { + job + }; + } + } + }); + }; + +}); diff --git a/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/index.js b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/index.js new file mode 100644 index 00000000000000..2dea7ffe6dff9e --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/index.js @@ -0,0 +1,12 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + +import './datafeed_service'; +import './job_timepicker_modal_controller'; +import './styles/main.less'; +import 'plugins/ml/jobs/new_job/simple/components/watcher'; diff --git a/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal.html b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal.html new file mode 100644 index 00000000000000..e9955e4a4b0048 --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal.html @@ -0,0 +1,109 @@ +
+ +

Start datafeed for {{jobId}}

+ +
+ + + +
+
+ +
+
+ +
+ + + + +
diff --git a/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal_controller.js b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal_controller.js new file mode 100644 index 00000000000000..ab6351f9b92bdc --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/job_timepicker_modal_controller.js @@ -0,0 +1,142 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + + +import moment from 'moment'; +import angular from 'angular'; + +import { mlJobService } from 'plugins/ml/services/job_service'; +import { mlMessageBarService } from 'plugins/ml/components/messagebar/messagebar_service'; +import { xpackFeatureProvider } from 'plugins/ml/license/check_license'; + +import { uiModules } from 'ui/modules'; +const module = uiModules.get('apps/ml'); + +module.controller('MlJobTimepickerModal', function ( + $scope, + $rootScope, + $modalInstance, + params, + Private) { + const msgs = mlMessageBarService; + $scope.saveLock = false; + const xpackFeature = Private(xpackFeatureProvider); + $scope.watcherEnabled = xpackFeature.isAvailable('watcher'); + + const job = angular.copy(params.job); + $scope.jobId = job.job_id; + + $scope.datafeedId = mlJobService.getDatafeedId(job.job_id); + + $scope.start = ''; + $scope.end = ''; + + let lastTime = ''; + if (job.data_counts && job.data_counts.latest_record_timestamp) { + const time = moment(job.data_counts.latest_record_timestamp); + lastTime = time.format('YYYY-MM-DD HH:mm:ss'); + } + + $scope.isNew = (job.data_counts && job.data_counts.input_record_count > 0) ? false : true; + + $scope.ui = { + lastTime: lastTime, + startDateText: '', + startRadio: '1', + endDateText: '', + endRadio: '1', + timepicker: { + from: '', + to: moment() + }, + setStartRadio: function (i) { + $scope.ui.startRadio = i; + }, + createWatch: false + }; + + function extractForm() { + if ($scope.ui.startRadio === '0') { + $scope.start = 'now'; + } + else if ($scope.ui.startRadio === '1') { + $scope.start = '0'; + } + else if ($scope.ui.startRadio === '2') { + $scope.start = moment($scope.ui.timepicker.from).unix() * 1000; + } + + if ($scope.ui.endRadio === '0') { + $scope.end = undefined; + } else if ($scope.ui.endRadio === '1') { + $scope.end = moment($scope.ui.timepicker.to).unix() * 1000; + } + } + + $scope.save = function () { + $scope.saveLock = true; + + extractForm(); + + let doStartCalled = false; + // in 10s call the function to start the datafeed. + // if the job has already opened and doStart has already been called, nothing will happen. + // However, if the job is still waiting to be opened, the datafeed can be started anyway. + window.setTimeout(doStart, 10000); + + // Attempt to open the job first. + // If it's already open, ignore the 409 error + mlJobService.openJob($scope.jobId) + .then(() => { + doStart(); + }) + .catch((resp) => { + if (resp.statusCode === 409) { + doStart(); + } else { + if (resp.statusCode === 500) { + if (doStartCalled === false) { + // doStart hasn't been called yet, this 500 has returned before 10s, + // so it's not due to a timeout + msgs.error(`Could not open ${$scope.jobId}`, resp); + } + } else { + // console.log(resp); + msgs.error(`Could not open ${$scope.jobId}`, resp); + } + $scope.saveLock = false; + } + }); + + // start the datafeed + function doStart() { + if (doStartCalled === false) { + doStartCalled = true; + mlJobService.startDatafeed($scope.datafeedId, $scope.jobId, $scope.start, $scope.end) + .then(() => { + $rootScope.$broadcast('jobsUpdated'); + + if ($scope.ui.createWatch) { + $rootScope.$broadcast('openCreateWatchWindow', job); + } + }) + .catch(() => { + $scope.saveLock = false; + }); + } + } + + $modalInstance.close(); + window.setTimeout(() => { + $rootScope.$broadcast('jobsUpdated'); + }, 500); + }; + + $scope.cancel = function () { + $modalInstance.close(); + }; +}); diff --git a/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/styles/main.less b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/styles/main.less new file mode 100644 index 00000000000000..53ee2b22bd73d7 --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/components/job_timepicker_modal/styles/main.less @@ -0,0 +1,85 @@ +.job-timepicker-modal { + font-size: 14px; + padding:20px; + cursor: auto; + + h3 { + overflow: hidden; + text-overflow: ellipsis; + } + + .date_container { + width: 200px; + display: inline-block; + } + + .ml-timepicker-contents { + margin-top: 5px; + + .btn-info.active, .kuiButton--primary.active { + color: #ffffff; + background-color: #154751; + border-color: #134049; + span { + color: #ffffff; + } + } + + .btn-default, .kuiButton--basic { + background: transparent; + color: #444444; + border: 0px; + box-shadow: none; + text-shadow: none; + } + + [ml-time-input] { + text-align: center; + } + + label { + display: block; + } + } + + .ml-timepicker-modes { + text-transform: capitalize; + } + .ml-timepicker-section { + float: left; + padding: 0px 15px; + min-width: 294px; + width: 294px; + border-left: 1px solid #FFFFFF; + border-right: 1px solid #FFFFFF; + + .ml-timepicker { + padding: 13px; + padding-top: none; + border: 2px solid #ecf0f1; + border-radius: 4px; + border-radius: 4px; + border-top-left-radius: 0px; + border-top-right-radius: 0px; + border-top: none; + + .btn, .kuiButton { + padding-left: 8px; + padding-right: 8px; + } + } + + .ml-timepicker-radio-bottom { + border-bottom-left-radius: 0px; + border-bottom-right-radius: 0px; + } + } + + .ml-timepicker-left-border { + border-left: 1px solid #ecf0f1; + } + + .ml-timepicker-right-border { + border-right: 1px solid #ecf0f1; + } +} diff --git a/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js b/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js index 7a578c274b1f6d..f12253e11f0bce 100644 --- a/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js +++ b/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js @@ -12,3 +12,4 @@ import './detectors_list_directive'; import './save_status_modal'; import './field_select_directive'; import 'plugins/ml/components/job_group_select'; +import '../../components/job_timepicker_modal'; From 578dc0e7cae3dcd13457181b9cba69feb7a548ce Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 7 Nov 2018 13:59:39 +0100 Subject: [PATCH 2/3] [ML] Added a karma/mocha test to verify dependencies are loaded correctly for new_job_controller. --- .../advanced/__tests__/new_job_controller.js | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 x-pack/plugins/ml/public/jobs/new_job/advanced/__tests__/new_job_controller.js diff --git a/x-pack/plugins/ml/public/jobs/new_job/advanced/__tests__/new_job_controller.js b/x-pack/plugins/ml/public/jobs/new_job/advanced/__tests__/new_job_controller.js new file mode 100644 index 00000000000000..11224b9b5f96b0 --- /dev/null +++ b/x-pack/plugins/ml/public/jobs/new_job/advanced/__tests__/new_job_controller.js @@ -0,0 +1,37 @@ +/* + * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one + * or more contributor license agreements. Licensed under the Elastic License; + * you may not use this file except in compliance with the Elastic License. + */ + + +import ngMock from 'ng_mock'; +import expect from 'expect.js'; +import sinon from 'sinon'; + +// Import this way to be able to stub/mock `createSearchItems` later on in the test using sinon. +import * as newJobUtils from 'plugins/ml/jobs/new_job/utils/new_job_utils'; + +describe('ML - Advanced Job Wizard - New Job Controller', () => { + beforeEach(() => { + ngMock.module('kibana'); + }); + + it('Initialize New Job Controller', (done) => { + sinon.stub(newJobUtils, 'createSearchItems').callsFake(() => ({ + indexPattern: {}, + savedSearch: {}, + combinedQuery: {} + })); + ngMock.inject(function ($rootScope, $controller) { + const scope = $rootScope.$new(); + $controller('MlNewJob', { $scope: scope }); + + // This is just about initializing the controller and making sure + // all angularjs based dependencies get loaded without error. + // This simple scope test is just a final sanity check. + expect(scope.ui.pageTitle).to.be('Create a new job'); + done(); + }); + }); +}); From 7f583efb4d83543c43007f2f7dfda992552e1868 Mon Sep 17 00:00:00 2001 From: Walter Rafelsberger Date: Wed, 7 Nov 2018 14:23:10 +0100 Subject: [PATCH 3/3] [ML] Uses consistent import style. --- x-pack/plugins/ml/public/jobs/new_job/advanced/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js b/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js index f12253e11f0bce..1e03ccde621df1 100644 --- a/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js +++ b/x-pack/plugins/ml/public/jobs/new_job/advanced/index.js @@ -12,4 +12,4 @@ import './detectors_list_directive'; import './save_status_modal'; import './field_select_directive'; import 'plugins/ml/components/job_group_select'; -import '../../components/job_timepicker_modal'; +import 'plugins/ml/jobs/components/job_timepicker_modal';