mirror of
https://github.com/elastic/kibana.git
synced 2025-04-25 10:23:14 -04:00
[6.x] [ML] Removing angular services refactor (#18654)
This commit is contained in:
parent
641f9da5af
commit
6f30c7b905
67 changed files with 3568 additions and 3595 deletions
|
@ -17,11 +17,7 @@ import 'plugins/ml/factories/listener_factory';
|
||||||
import 'plugins/ml/factories/state_factory';
|
import 'plugins/ml/factories/state_factory';
|
||||||
import 'plugins/ml/lib/angular_bootstrap_patch';
|
import 'plugins/ml/lib/angular_bootstrap_patch';
|
||||||
import 'plugins/ml/jobs';
|
import 'plugins/ml/jobs';
|
||||||
import 'plugins/ml/services/ml_clipboard_service';
|
|
||||||
import 'plugins/ml/services/job_service';
|
|
||||||
import 'plugins/ml/services/calendar_service';
|
import 'plugins/ml/services/calendar_service';
|
||||||
import 'plugins/ml/services/ml_api_service';
|
|
||||||
import 'plugins/ml/services/results_service';
|
|
||||||
import 'plugins/ml/components/messagebar';
|
import 'plugins/ml/components/messagebar';
|
||||||
import 'plugins/ml/datavisualizer';
|
import 'plugins/ml/datavisualizer';
|
||||||
import 'plugins/ml/explorer';
|
import 'plugins/ml/explorer';
|
||||||
|
|
|
@ -27,14 +27,16 @@ import {
|
||||||
showTypicalForFunction,
|
showTypicalForFunction,
|
||||||
getSeverity
|
getSeverity
|
||||||
} from 'plugins/ml/util/anomaly_utils';
|
} from 'plugins/ml/util/anomaly_utils';
|
||||||
|
import { getFieldTypeFromMapping } from 'plugins/ml/services/mapping_service';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
import template from './anomalies_table.html';
|
import template from './anomalies_table.html';
|
||||||
|
|
||||||
import 'plugins/ml/components/controls';
|
import 'plugins/ml/components/controls';
|
||||||
import 'plugins/ml/components/paginated_table';
|
import 'plugins/ml/components/paginated_table';
|
||||||
import 'plugins/ml/filters/format_value';
|
import 'plugins/ml/filters/format_value';
|
||||||
import 'plugins/ml/filters/metric_change_description';
|
import 'plugins/ml/filters/metric_change_description';
|
||||||
import 'plugins/ml/services/job_service';
|
|
||||||
import 'plugins/ml/services/mapping_service';
|
|
||||||
import './expanded_row/expanded_row_directive';
|
import './expanded_row/expanded_row_directive';
|
||||||
import './influencers_cell/influencers_cell_directive';
|
import './influencers_cell/influencers_cell_directive';
|
||||||
|
|
||||||
|
@ -48,13 +50,10 @@ module.directive('mlAnomaliesTable', function (
|
||||||
$window,
|
$window,
|
||||||
$route,
|
$route,
|
||||||
timefilter,
|
timefilter,
|
||||||
mlJobService,
|
Private,
|
||||||
mlESMappingService,
|
|
||||||
mlResultsService,
|
|
||||||
mlAnomaliesTableService,
|
mlAnomaliesTableService,
|
||||||
mlSelectIntervalService,
|
mlSelectIntervalService,
|
||||||
mlSelectSeverityService,
|
mlSelectSeverityService,
|
||||||
mlFieldFormatService,
|
|
||||||
formatValueFilter) {
|
formatValueFilter) {
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
@ -77,6 +76,9 @@ module.directive('mlAnomaliesTable', function (
|
||||||
// just remove these resets.
|
// just remove these resets.
|
||||||
mlSelectIntervalService.state.reset().changed();
|
mlSelectIntervalService.state.reset().changed();
|
||||||
mlSelectSeverityService.state.reset().changed();
|
mlSelectSeverityService.state.reset().changed();
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlFieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
|
|
||||||
scope.momentInterval = 'second';
|
scope.momentInterval = 'second';
|
||||||
|
|
||||||
|
@ -195,7 +197,7 @@ module.directive('mlAnomaliesTable', function (
|
||||||
findFieldType(datafeedIndices[i]);
|
findFieldType(datafeedIndices[i]);
|
||||||
|
|
||||||
function findFieldType(index) {
|
function findFieldType(index) {
|
||||||
mlESMappingService.getFieldTypeFromMapping(index, categorizationFieldName)
|
getFieldTypeFromMapping(index, categorizationFieldName)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp !== '') {
|
if (resp !== '') {
|
||||||
createAndOpenUrl(index, resp);
|
createAndOpenUrl(index, resp);
|
||||||
|
|
|
@ -16,7 +16,7 @@ const module = uiModules.get('apps/ml');
|
||||||
module.service('mlConfirmModalService', function ($modal, $q) {
|
module.service('mlConfirmModalService', function ($modal, $q) {
|
||||||
|
|
||||||
this.open = function (options) {
|
this.open = function (options) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
$modal.open({
|
$modal.open({
|
||||||
template,
|
template,
|
||||||
controller: 'MlConfirmModal',
|
controller: 'MlConfirmModal',
|
||||||
|
@ -31,13 +31,13 @@ module.service('mlConfirmModalService', function ($modal, $q) {
|
||||||
okLabel: options.okLabel,
|
okLabel: options.okLabel,
|
||||||
cancelLabel: options.cancelLabel,
|
cancelLabel: options.cancelLabel,
|
||||||
hideCancel: options.hideCancel,
|
hideCancel: options.hideCancel,
|
||||||
ok: deferred.resolve,
|
ok: resolve,
|
||||||
cancel: deferred.reject,
|
cancel: reject,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,9 @@ import PropTypes from 'prop-types';
|
||||||
import React, { Component } from 'react';
|
import React, { Component } from 'react';
|
||||||
import { RecognizedResult } from './recognized_result';
|
import { RecognizedResult } from './recognized_result';
|
||||||
|
|
||||||
export function dataRecognizerProvider(ml) {
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
|
export function dataRecognizerProvider() {
|
||||||
|
|
||||||
class DataRecognizer extends Component {
|
class DataRecognizer extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
|
|
@ -15,7 +15,6 @@ import _ from 'lodash';
|
||||||
import d3 from 'd3';
|
import d3 from 'd3';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import 'plugins/ml/services/results_service';
|
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
import { numTicksForDateFormat } from 'plugins/ml/util/chart_utils';
|
import { numTicksForDateFormat } from 'plugins/ml/util/chart_utils';
|
||||||
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
||||||
|
|
|
@ -6,15 +6,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import moment from 'moment';
|
|
||||||
import template from './full_time_range_selector.html';
|
import template from './full_time_range_selector.html';
|
||||||
|
|
||||||
import { FieldsServiceProvider } from 'plugins/ml/services/fields_service';
|
import { FullTimeRangeSelectorServiceProvider } from 'plugins/ml/components/full_time_range_selector/full_time_range_selector_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlFullTimeRangeSelector', function (mlFullTimeRangeSelectorService) {
|
module.directive('mlFullTimeRangeSelector', function (Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
@ -25,33 +24,11 @@ module.directive('mlFullTimeRangeSelector', function (mlFullTimeRangeSelectorSer
|
||||||
query: '='
|
query: '='
|
||||||
},
|
},
|
||||||
controller: function ($scope) {
|
controller: function ($scope) {
|
||||||
|
const mlFullTimeRangeSelectorService = Private(FullTimeRangeSelectorServiceProvider);
|
||||||
|
|
||||||
$scope.setFullTimeRange = function () {
|
$scope.setFullTimeRange = function () {
|
||||||
mlFullTimeRangeSelectorService.setFullTimeRange($scope.indexPattern, $scope.query);
|
mlFullTimeRangeSelectorService.setFullTimeRange($scope.indexPattern, $scope.query);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})
|
});
|
||||||
.service('mlFullTimeRangeSelectorService', function (
|
|
||||||
timefilter,
|
|
||||||
Notifier,
|
|
||||||
Private) {
|
|
||||||
|
|
||||||
const notify = new Notifier();
|
|
||||||
const fieldsService = Private(FieldsServiceProvider);
|
|
||||||
|
|
||||||
// called on button press
|
|
||||||
this.setFullTimeRange = function (indexPattern, query) {
|
|
||||||
// load the earliest and latest time stamps for the index
|
|
||||||
fieldsService.getTimeFieldRange(
|
|
||||||
indexPattern.title,
|
|
||||||
indexPattern.timeFieldName,
|
|
||||||
query)
|
|
||||||
.then((resp) => {
|
|
||||||
timefilter.time.from = moment(resp.start.epoch).toISOString();
|
|
||||||
timefilter.time.to = moment(resp.end.epoch).toISOString();
|
|
||||||
})
|
|
||||||
.catch((resp) => {
|
|
||||||
notify.error(resp);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
/*
|
||||||
|
* 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 { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
|
export function FullTimeRangeSelectorServiceProvider(timefilter, Notifier, $q) {
|
||||||
|
const notify = new Notifier();
|
||||||
|
|
||||||
|
function setFullTimeRange(indexPattern, query) {
|
||||||
|
// load the earliest and latest time stamps for the index
|
||||||
|
$q.when(ml.getTimeFieldRange({
|
||||||
|
index: indexPattern.title,
|
||||||
|
timeFieldName: indexPattern.timeFieldName,
|
||||||
|
query
|
||||||
|
}))
|
||||||
|
.then((resp) => {
|
||||||
|
timefilter.time.from = moment(resp.start.epoch).toISOString();
|
||||||
|
timefilter.time.to = moment(resp.end.epoch).toISOString();
|
||||||
|
})
|
||||||
|
.catch((resp) => {
|
||||||
|
notify.error(resp);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
setFullTimeRange
|
||||||
|
};
|
||||||
|
}
|
|
@ -10,11 +10,13 @@ import _ from 'lodash';
|
||||||
|
|
||||||
import template from './job_group_select.html';
|
import template from './job_group_select.html';
|
||||||
|
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CalendarServiceProvider } from 'plugins/ml/services/calendar_service';
|
||||||
import { InitAfterBindingsWorkaround } from 'ui/compat';
|
import { InitAfterBindingsWorkaround } from 'ui/compat';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlJobGroupSelect', function (es, ml, $timeout, mlJobService, mlCalendarService) {
|
module.directive('mlJobGroupSelect', function (es, $timeout, Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'E',
|
restrict: 'E',
|
||||||
template,
|
template,
|
||||||
|
@ -26,7 +28,10 @@ module.directive('mlJobGroupSelect', function (es, ml, $timeout, mlJobService, m
|
||||||
controllerAs: 'mlGroupSelect',
|
controllerAs: 'mlGroupSelect',
|
||||||
bindToController: true,
|
bindToController: true,
|
||||||
controller: class MlGroupSelectController extends InitAfterBindingsWorkaround {
|
controller: class MlGroupSelectController extends InitAfterBindingsWorkaround {
|
||||||
|
|
||||||
initAfterBindings($scope) {
|
initAfterBindings($scope) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlCalendarService = Private(CalendarServiceProvider);
|
||||||
this.$scope = $scope;
|
this.$scope = $scope;
|
||||||
this.selectedGroups = [];
|
this.selectedGroups = [];
|
||||||
this.groups = [];
|
this.groups = [];
|
||||||
|
|
|
@ -18,17 +18,19 @@ import d3 from 'd3';
|
||||||
|
|
||||||
import template from './job_select_list.html';
|
import template from './job_select_list.html';
|
||||||
import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlJobSelectList', function (mlJobService, mlJobSelectService, timefilter) {
|
module.directive('mlJobSelectList', function (Private, mlJobSelectService, timefilter) {
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: true,
|
replace: true,
|
||||||
transclude: true,
|
transclude: true,
|
||||||
template,
|
template,
|
||||||
controller: function ($scope) {
|
controller: function ($scope) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
$scope.jobs = [];
|
$scope.jobs = [];
|
||||||
$scope.groups = [];
|
$scope.groups = [];
|
||||||
$scope.homelessJobs = [];
|
$scope.homelessJobs = [];
|
||||||
|
|
|
@ -11,10 +11,13 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { notify } from 'ui/notify';
|
import { notify } from 'ui/notify';
|
||||||
|
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.service('mlJobSelectService', function ($rootScope, mlJobService, globalState) {
|
module.service('mlJobSelectService', function ($rootScope, Private, globalState) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
const self = this;
|
const self = this;
|
||||||
|
|
||||||
|
|
|
@ -9,13 +9,15 @@
|
||||||
|
|
||||||
import 'ngreact';
|
import 'ngreact';
|
||||||
|
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml', ['react']);
|
const module = uiModules.get('apps/ml', ['react']);
|
||||||
|
|
||||||
import { ValidateJob } from './validate_job_view';
|
import { ValidateJob } from './validate_job_view';
|
||||||
|
|
||||||
module.directive('mlValidateJob', function ($injector) {
|
module.directive('mlValidateJob', function ($injector) {
|
||||||
const mlJobService = $injector.get('mlJobService');
|
const Private = $injector.get('Private');
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
const reactDirective = $injector.get('reactDirective');
|
const reactDirective = $injector.get('reactDirective');
|
||||||
|
|
||||||
return reactDirective(
|
return reactDirective(
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
||||||
import { createSearchItems } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
import { createSearchItems } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
import { getIndexPatternWithRoute, getSavedSearchWithRoute, timeBasedIndexCheck } from 'plugins/ml/util/index_utils';
|
import { getIndexPatternWithRoute, getSavedSearchWithRoute, timeBasedIndexCheck } from 'plugins/ml/util/index_utils';
|
||||||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
import template from './datavisualizer.html';
|
import template from './datavisualizer.html';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
|
@ -55,10 +56,10 @@ module
|
||||||
$route,
|
$route,
|
||||||
$timeout,
|
$timeout,
|
||||||
$window,
|
$window,
|
||||||
|
$q,
|
||||||
Private,
|
Private,
|
||||||
timefilter,
|
timefilter,
|
||||||
AppState,
|
AppState) {
|
||||||
ml) {
|
|
||||||
|
|
||||||
timefilter.enableTimeRangeSelector();
|
timefilter.enableTimeRangeSelector();
|
||||||
timefilter.enableAutoRefreshSelector();
|
timefilter.enableAutoRefreshSelector();
|
||||||
|
@ -461,7 +462,7 @@ module
|
||||||
buckets.setBarTarget(BAR_TARGET);
|
buckets.setBarTarget(BAR_TARGET);
|
||||||
const aggInterval = buckets.getInterval();
|
const aggInterval = buckets.getInterval();
|
||||||
|
|
||||||
ml.getVisualizerFieldStats({
|
$q.when(ml.getVisualizerFieldStats({
|
||||||
indexPatternTitle: indexPattern.title,
|
indexPatternTitle: indexPattern.title,
|
||||||
query: $scope.searchQuery,
|
query: $scope.searchQuery,
|
||||||
timeFieldName: indexPattern.timeFieldName,
|
timeFieldName: indexPattern.timeFieldName,
|
||||||
|
@ -470,7 +471,7 @@ module
|
||||||
samplerShardSize: $scope.samplerShardSize,
|
samplerShardSize: $scope.samplerShardSize,
|
||||||
interval: aggInterval.expression,
|
interval: aggInterval.expression,
|
||||||
fields: numberFields
|
fields: numberFields
|
||||||
})
|
}))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
// Add the metric stats to the existing stats in the corresponding card.
|
// Add the metric stats to the existing stats in the corresponding card.
|
||||||
_.each($scope.metricCards, (card) => {
|
_.each($scope.metricCards, (card) => {
|
||||||
|
@ -520,7 +521,7 @@ module
|
||||||
|
|
||||||
if (fields.length > 0) {
|
if (fields.length > 0) {
|
||||||
|
|
||||||
ml.getVisualizerFieldStats({
|
$q.when(ml.getVisualizerFieldStats({
|
||||||
indexPatternTitle: indexPattern.title,
|
indexPatternTitle: indexPattern.title,
|
||||||
query: $scope.searchQuery,
|
query: $scope.searchQuery,
|
||||||
fields: fields,
|
fields: fields,
|
||||||
|
@ -529,7 +530,7 @@ module
|
||||||
latest: $scope.latest,
|
latest: $scope.latest,
|
||||||
samplerShardSize: $scope.samplerShardSize,
|
samplerShardSize: $scope.samplerShardSize,
|
||||||
maxExamples: 10
|
maxExamples: 10
|
||||||
})
|
}))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
// Add the metric stats to the existing stats in the corresponding card.
|
// Add the metric stats to the existing stats in the corresponding card.
|
||||||
_.each($scope.fieldCards, (card) => {
|
_.each($scope.fieldCards, (card) => {
|
||||||
|
@ -575,7 +576,7 @@ module
|
||||||
// 2. List of aggregatable fields that do not exist in docs
|
// 2. List of aggregatable fields that do not exist in docs
|
||||||
// 3. List of non-aggregatable fields that do exist in docs.
|
// 3. List of non-aggregatable fields that do exist in docs.
|
||||||
// 4. List of non-aggregatable fields that do not exist in docs.
|
// 4. List of non-aggregatable fields that do not exist in docs.
|
||||||
ml.getVisualizerOverallStats({
|
$q.when(ml.getVisualizerOverallStats({
|
||||||
indexPatternTitle: indexPattern.title,
|
indexPatternTitle: indexPattern.title,
|
||||||
query: $scope.searchQuery,
|
query: $scope.searchQuery,
|
||||||
timeFieldName: indexPattern.timeFieldName,
|
timeFieldName: indexPattern.timeFieldName,
|
||||||
|
@ -584,7 +585,7 @@ module
|
||||||
latest: $scope.latest,
|
latest: $scope.latest,
|
||||||
aggregatableFields: aggregatableFields,
|
aggregatableFields: aggregatableFields,
|
||||||
nonAggregatableFields: nonAggregatableFields
|
nonAggregatableFields: nonAggregatableFields
|
||||||
})
|
}))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
$scope.overallStats = resp;
|
$scope.overallStats = resp;
|
||||||
createMetricCards();
|
createMetricCards();
|
||||||
|
|
|
@ -16,8 +16,10 @@ import _ from 'lodash';
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
import { buildConfigFromDetector } from 'plugins/ml/util/chart_config_builder';
|
import { buildConfigFromDetector } from 'plugins/ml/util/chart_config_builder';
|
||||||
import { mlEscape } from 'plugins/ml/util/string_utils';
|
import { mlEscape } from 'plugins/ml/util/string_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
export function explorerChartConfigBuilder(mlJobService) {
|
export function explorerChartConfigBuilder(Private) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
const compiledTooltip = _.template(
|
const compiledTooltip = _.template(
|
||||||
'<div class="explorer-chart-info-tooltip">job ID: <%= jobId %><br/>' +
|
'<div class="explorer-chart-info-tooltip">job ID: <%= jobId %><br/>' +
|
||||||
|
|
|
@ -24,18 +24,19 @@ import { TimeBucketsProvider } from 'ui/time_buckets';
|
||||||
import 'plugins/ml/filters/format_value';
|
import 'plugins/ml/filters/format_value';
|
||||||
import loadingIndicatorWrapperTemplate from 'plugins/ml/components/loading_indicator/loading_indicator_wrapper.html';
|
import loadingIndicatorWrapperTemplate from 'plugins/ml/components/loading_indicator/loading_indicator_wrapper.html';
|
||||||
import { mlEscape } from 'plugins/ml/util/string_utils';
|
import { mlEscape } from 'plugins/ml/util/string_utils';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlExplorerChart', function (
|
module.directive('mlExplorerChart', function (
|
||||||
Private,
|
|
||||||
formatValueFilter,
|
formatValueFilter,
|
||||||
mlChartTooltipService,
|
mlChartTooltipService,
|
||||||
mlSelectSeverityService,
|
Private,
|
||||||
mlFieldFormatService) {
|
mlSelectSeverityService) {
|
||||||
|
|
||||||
function link(scope, element) {
|
function link(scope, element) {
|
||||||
|
const mlFieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
console.log('ml-explorer-chart directive link series config:', scope.seriesConfig);
|
console.log('ml-explorer-chart directive link series config:', scope.seriesConfig);
|
||||||
if (typeof scope.seriesConfig === 'undefined') {
|
if (typeof scope.seriesConfig === 'undefined') {
|
||||||
// just return so the empty directive renders without an error later on
|
// just return so the empty directive renders without an error later on
|
||||||
|
|
|
@ -21,15 +21,16 @@ const module = uiModules.get('apps/ml');
|
||||||
import { explorerChartConfigBuilder } from './explorer_chart_config_builder';
|
import { explorerChartConfigBuilder } from './explorer_chart_config_builder';
|
||||||
import { chartLimits } from 'plugins/ml/util/chart_utils';
|
import { chartLimits } from 'plugins/ml/util/chart_utils';
|
||||||
import { isTimeSeriesViewDetector } from 'plugins/ml/../common/util/job_utils';
|
import { isTimeSeriesViewDetector } from 'plugins/ml/../common/util/job_utils';
|
||||||
import 'plugins/ml/services/results_service';
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
module.controller('MlExplorerChartsContainerController', function ($scope, $injector) {
|
module.controller('MlExplorerChartsContainerController', function ($scope, $injector) {
|
||||||
const Private = $injector.get('Private');
|
const Private = $injector.get('Private');
|
||||||
const mlJobService = $injector.get('mlJobService');
|
|
||||||
const mlExplorerDashboardService = $injector.get('mlExplorerDashboardService');
|
const mlExplorerDashboardService = $injector.get('mlExplorerDashboardService');
|
||||||
const mlResultsService = $injector.get('mlResultsService');
|
|
||||||
const mlSelectSeverityService = $injector.get('mlSelectSeverityService');
|
const mlSelectSeverityService = $injector.get('mlSelectSeverityService');
|
||||||
const $q = $injector.get('$q');
|
const $q = $injector.get('$q');
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
$scope.seriesToPlot = [];
|
$scope.seriesToPlot = [];
|
||||||
|
|
||||||
|
|
|
@ -20,9 +20,6 @@ import moment from 'moment';
|
||||||
import 'plugins/ml/components/anomalies_table';
|
import 'plugins/ml/components/anomalies_table';
|
||||||
import 'plugins/ml/components/influencers_list';
|
import 'plugins/ml/components/influencers_list';
|
||||||
import 'plugins/ml/components/job_select_list';
|
import 'plugins/ml/components/job_select_list';
|
||||||
import 'plugins/ml/services/field_format_service';
|
|
||||||
import 'plugins/ml/services/job_service';
|
|
||||||
import 'plugins/ml/services/results_service';
|
|
||||||
|
|
||||||
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
|
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
|
@ -34,6 +31,9 @@ import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
||||||
import { getIndexPatterns } from 'plugins/ml/util/index_utils';
|
import { getIndexPatterns } from 'plugins/ml/util/index_utils';
|
||||||
import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher';
|
import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher';
|
||||||
import { IntervalHelperProvider, getBoundsRoundedToInterval } from 'plugins/ml/util/ml_time_buckets';
|
import { IntervalHelperProvider, getBoundsRoundedToInterval } from 'plugins/ml/util/ml_time_buckets';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
.when('/explorer/?', {
|
.when('/explorer/?', {
|
||||||
|
@ -56,9 +56,6 @@ module.controller('MlExplorerController', function (
|
||||||
Private,
|
Private,
|
||||||
timefilter,
|
timefilter,
|
||||||
mlCheckboxShowChartsService,
|
mlCheckboxShowChartsService,
|
||||||
mlFieldFormatService,
|
|
||||||
mlJobService,
|
|
||||||
mlResultsService,
|
|
||||||
mlJobSelectService,
|
mlJobSelectService,
|
||||||
mlExplorerDashboardService,
|
mlExplorerDashboardService,
|
||||||
mlSelectLimitService,
|
mlSelectLimitService,
|
||||||
|
@ -72,6 +69,9 @@ module.controller('MlExplorerController', function (
|
||||||
|
|
||||||
const TimeBuckets = Private(IntervalHelperProvider);
|
const TimeBuckets = Private(IntervalHelperProvider);
|
||||||
const queryFilter = Private(FilterBarQueryFilterProvider);
|
const queryFilter = Private(FilterBarQueryFilterProvider);
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlFieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
|
|
||||||
let resizeTimeout = null;
|
let resizeTimeout = null;
|
||||||
|
|
||||||
|
|
|
@ -12,8 +12,10 @@ import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
|
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
||||||
import { replaceTokensInUrlValue } from 'plugins/ml/util/custom_url_utils';
|
import { replaceTokensInUrlValue } from 'plugins/ml/util/custom_url_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
export function CustomUrlEditorServiceProvider(es, mlJobService, $q) {
|
export function CustomUrlEditorServiceProvider(es, Private, $q) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
// Builds the full URL for testing out a custom URL configuration, which
|
// Builds the full URL for testing out a custom URL configuration, which
|
||||||
// may contain dollar delimited partition / influencer entity tokens and
|
// may contain dollar delimited partition / influencer entity tokens and
|
||||||
|
|
|
@ -5,11 +5,13 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import { CreateWatchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/watcher/create_watch_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.controller('MlCreateWatchModal', function ($scope, $modalInstance, params, mlMessageBarService, mlCreateWatchService) {
|
module.controller('MlCreateWatchModal', function ($scope, $modalInstance, params, mlMessageBarService, Private) {
|
||||||
|
const mlCreateWatchService = Private(CreateWatchServiceProvider);
|
||||||
const msgs = mlMessageBarService; // set a reference to the message bar service
|
const msgs = mlMessageBarService; // set a reference to the message bar service
|
||||||
msgs.clear();
|
msgs.clear();
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { parseInterval } from 'plugins/ml/../common/util/parse_interval';
|
||||||
import { CustomUrlEditorServiceProvider } from 'plugins/ml/jobs/components/custom_url_editor/custom_url_editor_service';
|
import { CustomUrlEditorServiceProvider } from 'plugins/ml/jobs/components/custom_url_editor/custom_url_editor_service';
|
||||||
import { isWebUrl } from 'plugins/ml/util/string_utils';
|
import { isWebUrl } from 'plugins/ml/util/string_utils';
|
||||||
import { newJobLimits } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
|
import { newJobLimits } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
@ -28,10 +29,10 @@ module.controller('MlEditJobModal', function (
|
||||||
$window,
|
$window,
|
||||||
params,
|
params,
|
||||||
Private,
|
Private,
|
||||||
mlJobService,
|
|
||||||
mlMessageBarService) {
|
mlMessageBarService) {
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
msgs.clear();
|
msgs.clear();
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
$scope.saveLock = false;
|
$scope.saveLock = false;
|
||||||
const refreshJob = params.pscope.refreshJob;
|
const refreshJob = params.pscope.refreshJob;
|
||||||
|
|
||||||
|
|
|
@ -7,9 +7,11 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { toLocaleString, detectorToString } from 'plugins/ml/util/string_utils';
|
import { toLocaleString, detectorToString } from 'plugins/ml/util/string_utils';
|
||||||
|
import { copyTextToClipboard } from 'plugins/ml/util/clipboard_utils';
|
||||||
import { JOB_STATE, DATAFEED_STATE } from 'plugins/ml/../common/constants/states';
|
import { JOB_STATE, DATAFEED_STATE } from 'plugins/ml/../common/constants/states';
|
||||||
import { ML_DATA_PREVIEW_COUNT } from 'plugins/ml/../common/util/job_utils';
|
import { ML_DATA_PREVIEW_COUNT } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
|
import { checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
import numeral from '@elastic/numeral';
|
import numeral from '@elastic/numeral';
|
||||||
import chrome from 'ui/chrome';
|
import chrome from 'ui/chrome';
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
@ -18,7 +20,7 @@ import template from './expanded_row.html';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlJobListExpandedRow', function ($location, mlMessageBarService, mlJobService, mlClipboardService) {
|
module.directive('mlJobListExpandedRow', function ($location, mlMessageBarService, Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: false,
|
replace: false,
|
||||||
|
@ -34,6 +36,7 @@ module.directive('mlJobListExpandedRow', function ($location, mlMessageBarServic
|
||||||
template,
|
template,
|
||||||
link: function ($scope, $element) {
|
link: function ($scope, $element) {
|
||||||
const msgs = mlMessageBarService; // set a reference to the message bar service
|
const msgs = mlMessageBarService; // set a reference to the message bar service
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
const TIME_FORMAT = 'YYYY-MM-DD HH:mm:ss';
|
||||||
const DATA_FORMAT = '0.0 b';
|
const DATA_FORMAT = '0.0 b';
|
||||||
|
|
||||||
|
@ -169,7 +172,7 @@ module.directive('mlJobListExpandedRow', function ($location, mlMessageBarServic
|
||||||
|
|
||||||
$scope.copyToClipboard = function (job) {
|
$scope.copyToClipboard = function (job) {
|
||||||
const newJob = angular.copy(job);
|
const newJob = angular.copy(job);
|
||||||
const success = mlClipboardService.copy(angular.toJson(newJob));
|
const success = copyTextToClipboard(angular.toJson(newJob));
|
||||||
if (success) {
|
if (success) {
|
||||||
// flash the background color of the json box
|
// flash the background color of the json box
|
||||||
// to show the contents has been copied.
|
// to show the contents has been copied.
|
||||||
|
|
|
@ -8,7 +8,8 @@
|
||||||
|
|
||||||
|
|
||||||
import 'ngreact';
|
import 'ngreact';
|
||||||
import 'plugins/ml/services/forecast_service';
|
|
||||||
|
import { ForecastServiceProvider } from 'plugins/ml/services/forecast_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml', ['react']);
|
const module = uiModules.get('apps/ml', ['react']);
|
||||||
|
@ -16,7 +17,8 @@ const module = uiModules.get('apps/ml', ['react']);
|
||||||
import { ForecastsTable } from './forecasts_table';
|
import { ForecastsTable } from './forecasts_table';
|
||||||
|
|
||||||
module.directive('mlForecastsTable', function ($injector) {
|
module.directive('mlForecastsTable', function ($injector) {
|
||||||
const mlForecastService = $injector.get('mlForecastService');
|
const Private = $injector.get('Private');
|
||||||
|
const mlForecastService = Private(ForecastServiceProvider);
|
||||||
const reactDirective = $injector.get('reactDirective');
|
const reactDirective = $injector.get('reactDirective');
|
||||||
|
|
||||||
return reactDirective(
|
return reactDirective(
|
||||||
|
|
|
@ -16,4 +16,3 @@ import './expanded_row';
|
||||||
import 'ui/directives/confirm_click';
|
import 'ui/directives/confirm_click';
|
||||||
import 'plugins/ml/components/paginated_table';
|
import 'plugins/ml/components/paginated_table';
|
||||||
import 'plugins/ml/components/validate_job';
|
import 'plugins/ml/components/validate_job';
|
||||||
import 'plugins/ml/services/notification_service';
|
|
||||||
|
|
|
@ -9,6 +9,9 @@
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import angular from 'angular';
|
import angular from 'angular';
|
||||||
|
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CreateWatchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/watcher/create_watch_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
|
@ -17,10 +20,11 @@ module.controller('MlJobTimepickerModal', function (
|
||||||
$rootScope,
|
$rootScope,
|
||||||
$modalInstance,
|
$modalInstance,
|
||||||
params,
|
params,
|
||||||
mlJobService,
|
Private,
|
||||||
mlCreateWatchService,
|
|
||||||
mlMessageBarService) {
|
mlMessageBarService) {
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlCreateWatchService = Private(CreateWatchServiceProvider);
|
||||||
$scope.saveLock = false;
|
$scope.saveLock = false;
|
||||||
$scope.watcherEnabled = mlCreateWatchService.isWatcherEnabled();
|
$scope.watcherEnabled = mlCreateWatchService.isWatcherEnabled();
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ import jobsListControlsHtml from './jobs_list_controls.html';
|
||||||
import jobsListArrow from 'plugins/ml/components/paginated_table/open.html';
|
import jobsListArrow from 'plugins/ml/components/paginated_table/open.html';
|
||||||
import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
import { isTimeSeriesViewJob } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { toLocaleString, mlEscape } from 'plugins/ml/util/string_utils';
|
import { toLocaleString, mlEscape } from 'plugins/ml/util/string_utils';
|
||||||
|
import { copyTextToClipboard } from 'plugins/ml/util/clipboard_utils';
|
||||||
|
|
||||||
import uiRoutes from 'ui/routes';
|
import uiRoutes from 'ui/routes';
|
||||||
import { checkLicense } from 'plugins/ml/license/check_license';
|
import { checkLicense } from 'plugins/ml/license/check_license';
|
||||||
|
@ -31,6 +32,9 @@ import createWatchTemplate from 'plugins/ml/jobs/jobs_list/create_watch_modal/cr
|
||||||
import { buttonsEnabledChecks } from 'plugins/ml/jobs/jobs_list/buttons_enabled_checks';
|
import { buttonsEnabledChecks } from 'plugins/ml/jobs/jobs_list/buttons_enabled_checks';
|
||||||
import { cloudServiceProvider } from 'plugins/ml/services/cloud_service';
|
import { cloudServiceProvider } from 'plugins/ml/services/cloud_service';
|
||||||
import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
|
import { loadNewJobDefaults } from 'plugins/ml/jobs/new_job/utils/new_job_defaults';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CalendarServiceProvider } from 'plugins/ml/services/calendar_service';
|
||||||
|
import { JobMessagesServiceProvider } from 'plugins/ml/services/job_messages_service';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
.when('/jobs/?', {
|
.when('/jobs/?', {
|
||||||
|
@ -60,11 +64,7 @@ module.controller('MlJobsList',
|
||||||
kbnUrl,
|
kbnUrl,
|
||||||
Private,
|
Private,
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlClipboardService,
|
mlDatafeedService) {
|
||||||
mlJobService,
|
|
||||||
mlCalendarService,
|
|
||||||
mlDatafeedService,
|
|
||||||
mlNotificationService) {
|
|
||||||
|
|
||||||
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
||||||
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
||||||
|
@ -85,6 +85,9 @@ module.controller('MlJobsList',
|
||||||
$scope.mlNodesAvailable = mlNodesAvailable();
|
$scope.mlNodesAvailable = mlNodesAvailable();
|
||||||
$scope.permissionToViewMlNodeCount = permissionToViewMlNodeCount();
|
$scope.permissionToViewMlNodeCount = permissionToViewMlNodeCount();
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlCalendarService = Private(CalendarServiceProvider);
|
||||||
|
const jobMessagesService = Private(JobMessagesServiceProvider);
|
||||||
const { isRunningOnCloud, getCloudId } = Private(cloudServiceProvider);
|
const { isRunningOnCloud, getCloudId } = Private(cloudServiceProvider);
|
||||||
$scope.isCloud = isRunningOnCloud();
|
$scope.isCloud = isRunningOnCloud();
|
||||||
$scope.cloudId = getCloudId();
|
$scope.cloudId = getCloudId();
|
||||||
|
@ -169,7 +172,7 @@ module.controller('MlJobsList',
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.copyToClipboard = function (job) {
|
$scope.copyToClipboard = function (job) {
|
||||||
const success = mlClipboardService.copy(angular.toJson(job));
|
const success = copyTextToClipboard(angular.toJson(job));
|
||||||
if (success) {
|
if (success) {
|
||||||
msgs.clear();
|
msgs.clear();
|
||||||
msgs.info(job.job_id + ' JSON copied to clipboard');
|
msgs.info(job.job_id + ' JSON copied to clipboard');
|
||||||
|
@ -436,7 +439,7 @@ module.controller('MlJobsList',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return mlNotificationService.getJobAuditMessages(fromRange, jobId)
|
return jobMessagesService.getJobAuditMessages(fromRange, jobId)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const messages = resp.messages;
|
const messages = resp.messages;
|
||||||
_.each(messages, (msg) => {
|
_.each(messages, (msg) => {
|
||||||
|
@ -488,7 +491,7 @@ module.controller('MlJobsList',
|
||||||
createTimes[job.job_id] = moment(job.create_time).valueOf();
|
createTimes[job.job_id] = moment(job.create_time).valueOf();
|
||||||
});
|
});
|
||||||
|
|
||||||
mlNotificationService.getAuditMessagesSummary()
|
jobMessagesService.getAuditMessagesSummary()
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
const messagesPerJob = resp.messagesPerJob;
|
const messagesPerJob = resp.messagesPerJob;
|
||||||
_.each(messagesPerJob, (job) => {
|
_.each(messagesPerJob, (job) => {
|
||||||
|
|
|
@ -12,7 +12,7 @@ import angular from 'angular';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.controller('MlDetectorFilterModal', function ($scope, $modalInstance, params, mlJobService, mlMessageBarService) {
|
module.controller('MlDetectorFilterModal', function ($scope, $modalInstance, params, mlMessageBarService) {
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
msgs.clear();
|
msgs.clear();
|
||||||
$scope.title = 'Add new filter';
|
$scope.title = 'Add new filter';
|
||||||
|
|
|
@ -16,11 +16,12 @@ import { detectorToString } from 'plugins/ml/util/string_utils';
|
||||||
import template from './detectors_list.html';
|
import template from './detectors_list.html';
|
||||||
import detectorModalTemplate from 'plugins/ml/jobs/new_job/advanced/detector_modal/detector_modal.html';
|
import detectorModalTemplate from 'plugins/ml/jobs/new_job/advanced/detector_modal/detector_modal.html';
|
||||||
import detectorFilterModalTemplate from 'plugins/ml/jobs/new_job/advanced/detector_filter_modal/detector_filter_modal.html';
|
import detectorFilterModalTemplate from 'plugins/ml/jobs/new_job/advanced/detector_filter_modal/detector_filter_modal.html';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlJobDetectorsList', function ($modal, $q, mlJobService) {
|
module.directive('mlJobDetectorsList', function ($modal, $q, Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: true,
|
replace: true,
|
||||||
|
@ -33,6 +34,7 @@ module.directive('mlJobDetectorsList', function ($modal, $q, mlJobService) {
|
||||||
},
|
},
|
||||||
template,
|
template,
|
||||||
controller: function ($scope) {
|
controller: function ($scope) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
$scope.addDetector = function (dtr, index) {
|
$scope.addDetector = function (dtr, index) {
|
||||||
if (dtr !== undefined) {
|
if (dtr !== undefined) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ import { checkLicense } from 'plugins/ml/license/check_license';
|
||||||
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
||||||
import template from './new_job.html';
|
import template from './new_job.html';
|
||||||
import saveStatusTemplate from 'plugins/ml/jobs/new_job/advanced/save_status_modal/save_status_modal.html';
|
import saveStatusTemplate from 'plugins/ml/jobs/new_job/advanced/save_status_modal/save_status_modal.html';
|
||||||
import { createSearchItems } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
import { createSearchItems, createJobForSaving } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
import { getIndexPatterns, getIndexPatternWithRoute, getSavedSearchWithRoute, timeBasedIndexCheck } from 'plugins/ml/util/index_utils';
|
import { getIndexPatterns, getIndexPatternWithRoute, getSavedSearchWithRoute, timeBasedIndexCheck } from 'plugins/ml/util/index_utils';
|
||||||
import { ML_JOB_FIELD_TYPES, ES_FIELD_TYPES } from 'plugins/ml/../common/constants/field_types';
|
import { ML_JOB_FIELD_TYPES, ES_FIELD_TYPES } from 'plugins/ml/../common/constants/field_types';
|
||||||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
|
@ -28,6 +28,8 @@ import {
|
||||||
ML_DATA_PREVIEW_COUNT,
|
ML_DATA_PREVIEW_COUNT,
|
||||||
basicJobValidation
|
basicJobValidation
|
||||||
} from 'plugins/ml/../common/util/job_utils';
|
} from 'plugins/ml/../common/util/job_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
.when('/jobs/new_job/advanced', {
|
.when('/jobs/new_job/advanced', {
|
||||||
|
@ -65,19 +67,15 @@ module.controller('MlNewJob',
|
||||||
$location,
|
$location,
|
||||||
$modal,
|
$modal,
|
||||||
$q,
|
$q,
|
||||||
$timeout,
|
|
||||||
courier,
|
courier,
|
||||||
es,
|
es,
|
||||||
ml,
|
|
||||||
Private,
|
Private,
|
||||||
timefilter,
|
timefilter,
|
||||||
esServerUrl,
|
|
||||||
mlJobService,
|
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlDatafeedService,
|
mlDatafeedService,
|
||||||
mlConfirmModalService) {
|
mlConfirmModalService) {
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
timefilter.disableTimeRangeSelector(); // remove time picker from top of page
|
||||||
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
timefilter.disableAutoRefreshSelector(); // remove time picker from top of page
|
||||||
const MODE = {
|
const MODE = {
|
||||||
|
@ -433,12 +431,6 @@ module.controller('MlNewJob',
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function createJobForSaving(job) {
|
|
||||||
const newJob = angular.copy(job);
|
|
||||||
delete newJob.datafeed_config;
|
|
||||||
return newJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveFunc() {
|
function saveFunc() {
|
||||||
|
|
||||||
if ($scope.ui.useDedicatedIndex) {
|
if ($scope.ui.useDedicatedIndex) {
|
||||||
|
|
|
@ -9,13 +9,12 @@
|
||||||
import template from './bucket_span_estimator.html';
|
import template from './bucket_span_estimator.html';
|
||||||
import { getQueryFromSavedSearch } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
import { getQueryFromSavedSearch } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlBucketSpanEstimator', function ($injector) {
|
module.directive('mlBucketSpanEstimator', function ($q) {
|
||||||
const ml = $injector.get('ml');
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: false,
|
replace: false,
|
||||||
|
@ -79,7 +78,7 @@ module.directive('mlBucketSpanEstimator', function ($injector) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ml.estimateBucketSpan(data)
|
$q.when(ml.estimateBucketSpan(data))
|
||||||
.then((interval) => {
|
.then((interval) => {
|
||||||
if (interval.error) {
|
if (interval.error) {
|
||||||
errorHandler(interval.message);
|
errorHandler(interval.message);
|
||||||
|
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import { PostSaveServiceProvider } from './post_save_service';
|
||||||
|
import { CreateWatchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/watcher/create_watch_service';
|
||||||
import template from './post_save_options.html';
|
import template from './post_save_options.html';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlPostSaveOptions', function (mlPostSaveService, mlCreateWatchService) {
|
module.directive('mlPostSaveOptions', function (Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: false,
|
replace: false,
|
||||||
|
@ -23,13 +25,16 @@ module.directive('mlPostSaveOptions', function (mlPostSaveService, mlCreateWatch
|
||||||
template,
|
template,
|
||||||
link: function ($scope) {
|
link: function ($scope) {
|
||||||
|
|
||||||
$scope.watcherEnabled = mlCreateWatchService.isWatcherEnabled();
|
const postSaveService = Private(PostSaveServiceProvider);
|
||||||
$scope.status = mlPostSaveService.status;
|
const createWatchService = Private(CreateWatchServiceProvider);
|
||||||
$scope.STATUS = mlPostSaveService.STATUS;
|
|
||||||
|
|
||||||
mlCreateWatchService.reset();
|
$scope.watcherEnabled = createWatchService.isWatcherEnabled();
|
||||||
|
$scope.status = postSaveService.status;
|
||||||
|
$scope.STATUS = postSaveService.STATUS;
|
||||||
|
|
||||||
mlCreateWatchService.config.includeInfluencers = $scope.includeInfluencers;
|
createWatchService.reset();
|
||||||
|
|
||||||
|
createWatchService.config.includeInfluencers = $scope.includeInfluencers;
|
||||||
$scope.runInRealtime = false;
|
$scope.runInRealtime = false;
|
||||||
$scope.createWatch = false;
|
$scope.createWatch = false;
|
||||||
$scope.embedded = true;
|
$scope.embedded = true;
|
||||||
|
@ -39,55 +44,8 @@ module.directive('mlPostSaveOptions', function (mlPostSaveService, mlCreateWatch
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.apply = function () {
|
$scope.apply = function () {
|
||||||
mlPostSaveService.apply($scope.jobId, $scope.runInRealtime, $scope.createWatch);
|
postSaveService.apply($scope.jobId, $scope.runInRealtime, $scope.createWatch);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}).service('mlPostSaveService', function (mlJobService, mlMessageBarService, $q, mlCreateWatchService) {
|
|
||||||
const msgs = mlMessageBarService;
|
|
||||||
this.STATUS = {
|
|
||||||
SAVE_FAILED: -1,
|
|
||||||
SAVING: 0,
|
|
||||||
SAVED: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.status = {
|
|
||||||
realtimeJob: null,
|
|
||||||
watch: null
|
|
||||||
};
|
|
||||||
mlCreateWatchService.status = this.status;
|
|
||||||
|
|
||||||
this.externalCreateWatch;
|
|
||||||
this.startRealtimeJob = function (jobId) {
|
|
||||||
const deferred = $q.defer();
|
|
||||||
this.status.realtimeJob = this.STATUS.SAVING;
|
|
||||||
|
|
||||||
const datafeedId = mlJobService.getDatafeedId(jobId);
|
|
||||||
|
|
||||||
mlJobService.openJob(jobId)
|
|
||||||
.finally(() => {
|
|
||||||
mlJobService.startDatafeed(datafeedId, jobId, 0, undefined)
|
|
||||||
.then(() => {
|
|
||||||
this.status.realtimeJob = this.STATUS.SAVED;
|
|
||||||
deferred.resolve();
|
|
||||||
}).catch((resp) => {
|
|
||||||
msgs.error('Could not start datafeed: ', resp);
|
|
||||||
this.status.realtimeJob = this.STATUS.SAVE_FAILED;
|
|
||||||
deferred.reject();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.apply = function (jobId, runInRealtime, createWatch) {
|
|
||||||
if (runInRealtime) {
|
|
||||||
this.startRealtimeJob(jobId)
|
|
||||||
.then(() => {
|
|
||||||
if (createWatch) {
|
|
||||||
mlCreateWatchService.createNewWatch(jobId);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/*
|
||||||
|
* 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 { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CreateWatchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/watcher/create_watch_service';
|
||||||
|
|
||||||
|
export function PostSaveServiceProvider(Private, mlMessageBarService, $q) {
|
||||||
|
const msgs = mlMessageBarService;
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const createWatchService = Private(CreateWatchServiceProvider);
|
||||||
|
|
||||||
|
class PostSaveService {
|
||||||
|
constructor() {
|
||||||
|
this.STATUS = {
|
||||||
|
SAVE_FAILED: -1,
|
||||||
|
SAVING: 0,
|
||||||
|
SAVED: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.status = {
|
||||||
|
realtimeJob: null,
|
||||||
|
watch: null
|
||||||
|
};
|
||||||
|
createWatchService.status = this.status;
|
||||||
|
|
||||||
|
this.externalCreateWatch;
|
||||||
|
}
|
||||||
|
|
||||||
|
startRealtimeJob(jobId) {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
|
this.status.realtimeJob = this.STATUS.SAVING;
|
||||||
|
|
||||||
|
const datafeedId = mlJobService.getDatafeedId(jobId);
|
||||||
|
|
||||||
|
mlJobService.openJob(jobId)
|
||||||
|
.finally(() => {
|
||||||
|
mlJobService.startDatafeed(datafeedId, jobId, 0, undefined)
|
||||||
|
.then(() => {
|
||||||
|
this.status.realtimeJob = this.STATUS.SAVED;
|
||||||
|
resolve();
|
||||||
|
}).catch((resp) => {
|
||||||
|
msgs.error('Could not start datafeed: ', resp);
|
||||||
|
this.status.realtimeJob = this.STATUS.SAVE_FAILED;
|
||||||
|
reject();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
apply(jobId, runInRealtime, createWatch) {
|
||||||
|
if (runInRealtime) {
|
||||||
|
this.startRealtimeJob(jobId)
|
||||||
|
.then(() => {
|
||||||
|
if (createWatch) {
|
||||||
|
createWatchService.createNewWatch(jobId);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new PostSaveService();
|
||||||
|
}
|
|
@ -10,7 +10,9 @@
|
||||||
// based on the cardinality of the field being used to split the data.
|
// based on the cardinality of the field being used to split the data.
|
||||||
// the limit should be 10MB plus 20kB per series, rounded up to the nearest MB.
|
// the limit should be 10MB plus 20kB per series, rounded up to the nearest MB.
|
||||||
|
|
||||||
export function CalculateModelMemoryLimitProvider(ml) {
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
|
export function CalculateModelMemoryLimitProvider() {
|
||||||
return function calculateModelMemoryLimit(
|
return function calculateModelMemoryLimit(
|
||||||
indexPattern,
|
indexPattern,
|
||||||
splitFieldName,
|
splitFieldName,
|
||||||
|
|
|
@ -11,9 +11,13 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
||||||
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
import { SimpleJobSearchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/utils/search_service';
|
||||||
|
|
||||||
export function ChartDataUtilsProvider($q, Private, timefilter, mlSimpleJobSearchService, mlResultsService) {
|
export function ChartDataUtilsProvider($q, Private, timefilter) {
|
||||||
const TimeBuckets = Private(IntervalHelperProvider);
|
const TimeBuckets = Private(IntervalHelperProvider);
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const mlSimpleJobSearchService = Private(SimpleJobSearchServiceProvider);
|
||||||
|
|
||||||
function loadDocCountData(formConfig, chartData) {
|
function loadDocCountData(formConfig, chartData) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
|
|
|
@ -11,13 +11,10 @@ import _ from 'lodash';
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
||||||
import { escapeForElasticsearchQuery } from 'plugins/ml/util/string_utils';
|
import { escapeForElasticsearchQuery } from 'plugins/ml/util/string_utils';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function SimpleJobSearchServiceProvider($q, es) {
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlSimpleJobSearchService', function ($q, es) {
|
|
||||||
// detector swimlane search
|
// detector swimlane search
|
||||||
this.getScoresByRecord = function (jobId, earliestMs, latestMs, interval, firstSplitField) {
|
function getScoresByRecord(jobId, earliestMs, latestMs, interval, firstSplitField) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
results: {}
|
results: {}
|
||||||
|
@ -116,16 +113,16 @@ module.service('mlSimpleJobSearchService', function ($q, es) {
|
||||||
obj.results[dtrIndex] = dtrResults;
|
obj.results[dtrIndex] = dtrResults;
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getCategoryFields = function (index, field, size, query) {
|
function getCategoryFields(index, field, size, query) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
results: {}
|
results: {}
|
||||||
|
@ -153,13 +150,18 @@ module.service('mlSimpleJobSearchService', function ($q, es) {
|
||||||
obj.results.values.push(f.key);
|
obj.results.values.push(f.key);
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getScoresByRecord,
|
||||||
|
getCategoryFields
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
}
|
||||||
|
|
|
@ -8,13 +8,15 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
|
import { CreateWatchServiceProvider } from 'plugins/ml/jobs/new_job/simple/components/watcher/create_watch_service';
|
||||||
|
|
||||||
import template from './create_watch.html';
|
import template from './create_watch.html';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.directive('mlCreateWatch', function (es, ml, mlCreateWatchService) {
|
module.directive('mlCreateWatch', function (es, $q, Private) {
|
||||||
return {
|
return {
|
||||||
restrict: 'AE',
|
restrict: 'AE',
|
||||||
replace: false,
|
replace: false,
|
||||||
|
@ -25,7 +27,7 @@ module.directive('mlCreateWatch', function (es, ml, mlCreateWatchService) {
|
||||||
},
|
},
|
||||||
template,
|
template,
|
||||||
link: function ($scope) {
|
link: function ($scope) {
|
||||||
|
const mlCreateWatchService = Private(CreateWatchServiceProvider);
|
||||||
$scope.config = mlCreateWatchService.config;
|
$scope.config = mlCreateWatchService.config;
|
||||||
$scope.status = mlCreateWatchService.status;
|
$scope.status = mlCreateWatchService.status;
|
||||||
$scope.STATUS = mlCreateWatchService.STATUS;
|
$scope.STATUS = mlCreateWatchService.STATUS;
|
||||||
|
@ -56,7 +58,7 @@ module.directive('mlCreateWatch', function (es, ml, mlCreateWatchService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// load elasticsearch settings to see if email has been configured
|
// load elasticsearch settings to see if email has been configured
|
||||||
ml.getNotificationSettings().then((resp) => {
|
$q.when(ml.getNotificationSettings()).then((resp) => {
|
||||||
if (_.has(resp, 'defaults.xpack.notification.email')) {
|
if (_.has(resp, 'defaults.xpack.notification.email')) {
|
||||||
$scope.ui.emailEnabled = true;
|
$scope.ui.emailEnabled = true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,38 +14,10 @@ import emailBody from './email.html';
|
||||||
import emailInfluencersBody from './email-influencers.html';
|
import emailInfluencersBody from './email-influencers.html';
|
||||||
import { watch } from './watch.js';
|
import { watch } from './watch.js';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function CreateWatchServiceProvider($http, $q, Private) {
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
module.service('mlCreateWatchService', function ($http, $q, Private) {
|
|
||||||
|
|
||||||
const xpackInfo = Private(XPackInfoProvider);
|
const xpackInfo = Private(XPackInfoProvider);
|
||||||
|
|
||||||
this.config = {};
|
|
||||||
|
|
||||||
this.STATUS = {
|
|
||||||
SAVE_FAILED: -1,
|
|
||||||
SAVING: 0,
|
|
||||||
SAVED: 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
this.status = {
|
|
||||||
realtimeJob: null,
|
|
||||||
watch: null
|
|
||||||
};
|
|
||||||
|
|
||||||
this.reset = function () {
|
|
||||||
this.status.realtimeJob = null;
|
|
||||||
this.status.watch = null;
|
|
||||||
|
|
||||||
this.config.id = '';
|
|
||||||
this.config.includeEmail = false;
|
|
||||||
this.config.email = '';
|
|
||||||
this.config.interval = '20m';
|
|
||||||
this.config.watcherEditURL = '';
|
|
||||||
this.config.includeInfluencers = false;
|
|
||||||
this.config.threshold = { display: 'critical', val: 75 };
|
|
||||||
};
|
|
||||||
|
|
||||||
const compiledEmailBody = _.template(emailBody);
|
const compiledEmailBody = _.template(emailBody);
|
||||||
|
|
||||||
const emailSection = {
|
const emailSection = {
|
||||||
|
@ -67,8 +39,48 @@ module.service('mlCreateWatchService', function ($http, $q, Private) {
|
||||||
return Math.floor(Math.random() * (max - min + 1) + min);
|
return Math.floor(Math.random() * (max - min + 1) + min);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.createNewWatch = function (jobId) {
|
function saveWatch(watchModel) {
|
||||||
const deferred = $q.defer();
|
const basePath = chrome.addBasePath('/api/watcher');
|
||||||
|
const url = `${basePath}/watch/${watchModel.id}`;
|
||||||
|
|
||||||
|
return $http.put(url, watchModel.upstreamJSON)
|
||||||
|
.catch(e => {
|
||||||
|
throw e.data.message;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CreateWatchService {
|
||||||
|
constructor() {
|
||||||
|
this.config = {};
|
||||||
|
|
||||||
|
this.STATUS = {
|
||||||
|
SAVE_FAILED: -1,
|
||||||
|
SAVING: 0,
|
||||||
|
SAVED: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.status = {
|
||||||
|
realtimeJob: null,
|
||||||
|
watch: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.status.realtimeJob = null;
|
||||||
|
this.status.watch = null;
|
||||||
|
|
||||||
|
this.config.id = '';
|
||||||
|
this.config.includeEmail = false;
|
||||||
|
this.config.email = '';
|
||||||
|
this.config.interval = '20m';
|
||||||
|
this.config.watcherEditURL = '';
|
||||||
|
this.config.includeInfluencers = false;
|
||||||
|
this.config.threshold = { display: 'critical', val: 75 };
|
||||||
|
}
|
||||||
|
|
||||||
|
createNewWatch = function (jobId) {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
this.status.watch = this.STATUS.SAVING;
|
this.status.watch = this.STATUS.SAVING;
|
||||||
if (jobId !== undefined) {
|
if (jobId !== undefined) {
|
||||||
const id = `ml-${jobId}`;
|
const id = `ml-${jobId}`;
|
||||||
|
@ -95,7 +107,7 @@ module.service('mlCreateWatchService', function ($http, $q, Private) {
|
||||||
|
|
||||||
// set the trigger interval to be a random number between 60 and 120 seconds
|
// set the trigger interval to be a random number between 60 and 120 seconds
|
||||||
// this is to avoid all watches firing at once if the server restarts
|
// this is to avoid all watches firing at once if the server restarts
|
||||||
// and the watches synchronise
|
// and the watches synchronize
|
||||||
const triggerInterval = randomNumber(60, 120);
|
const triggerInterval = randomNumber(60, 120);
|
||||||
watch.trigger.schedule.interval = `${triggerInterval}s`;
|
watch.trigger.schedule.interval = `${triggerInterval}s`;
|
||||||
|
|
||||||
|
@ -114,35 +126,25 @@ module.service('mlCreateWatchService', function ($http, $q, Private) {
|
||||||
this.status.watch = this.STATUS.SAVED;
|
this.status.watch = this.STATUS.SAVED;
|
||||||
this.config.watcherEditURL =
|
this.config.watcherEditURL =
|
||||||
`${chrome.getBasePath()}/app/kibana#/management/elasticsearch/watcher/watches/watch/${id}/edit?_g=()`;
|
`${chrome.getBasePath()}/app/kibana#/management/elasticsearch/watcher/watches/watch/${id}/edit?_g=()`;
|
||||||
deferred.resolve();
|
resolve();
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
this.status.watch = this.STATUS.SAVE_FAILED;
|
this.status.watch = this.STATUS.SAVE_FAILED;
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.status.watch = this.STATUS.SAVE_FAILED;
|
this.status.watch = this.STATUS.SAVE_FAILED;
|
||||||
deferred.reject();
|
reject();
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
function saveWatch(watchModel) {
|
|
||||||
const basePath = chrome.addBasePath('/api/watcher');
|
|
||||||
const url = `${basePath}/watch/${watchModel.id}`;
|
|
||||||
|
|
||||||
return $http.put(url, watchModel.upstreamJSON)
|
|
||||||
.catch(e => {
|
|
||||||
throw e.data.message;
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
this.isWatcherEnabled = function () {
|
isWatcherEnabled() {
|
||||||
return xpackInfo.get('features.watcher.isAvailable', false);
|
return xpackInfo.get('features.watcher.isAvailable', false);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.loadWatch = function (jobId) {
|
loadWatch(jobId) {
|
||||||
const id = `ml-${jobId}`;
|
const id = `ml-${jobId}`;
|
||||||
const basePath = chrome.addBasePath('/api/watcher');
|
const basePath = chrome.addBasePath('/api/watcher');
|
||||||
const url = `${basePath}/watch/${id}`;
|
const url = `${basePath}/watch/${id}`;
|
||||||
|
@ -150,7 +152,8 @@ module.service('mlCreateWatchService', function ($http, $q, Private) {
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
throw e.data.message;
|
throw e.data.message;
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new CreateWatchService();
|
||||||
});
|
}
|
||||||
|
|
|
@ -37,6 +37,9 @@ import {
|
||||||
createResultsUrl,
|
createResultsUrl,
|
||||||
addNewJobToRecentlyAccessed,
|
addNewJobToRecentlyAccessed,
|
||||||
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { MultiMetricJobServiceProvider } from './create_job_service';
|
||||||
|
import { FullTimeRangeSelectorServiceProvider } from 'plugins/ml/components/full_time_range_selector/full_time_range_selector_service';
|
||||||
import template from './create_job.html';
|
import template from './create_job.html';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
|
@ -61,10 +64,7 @@ module
|
||||||
$route,
|
$route,
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private,
|
||||||
mlJobService,
|
|
||||||
mlMultiMetricJobService,
|
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlFullTimeRangeSelectorService,
|
|
||||||
AppState) {
|
AppState) {
|
||||||
|
|
||||||
timefilter.enableTimeRangeSelector();
|
timefilter.enableTimeRangeSelector();
|
||||||
|
@ -74,6 +74,9 @@ module
|
||||||
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
||||||
const calculateModelMemoryLimit = Private(CalculateModelMemoryLimitProvider);
|
const calculateModelMemoryLimit = Private(CalculateModelMemoryLimitProvider);
|
||||||
const chartDataUtils = Private(ChartDataUtilsProvider);
|
const chartDataUtils = Private(ChartDataUtilsProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlMultiMetricJobService = Private(MultiMetricJobServiceProvider);
|
||||||
|
const mlFullTimeRangeSelectorService = Private(FullTimeRangeSelectorServiceProvider);
|
||||||
$scope.addNewJobToRecentlyAccessed = addNewJobToRecentlyAccessed;
|
$scope.addNewJobToRecentlyAccessed = addNewJobToRecentlyAccessed;
|
||||||
|
|
||||||
const stateDefaults = {
|
const stateDefaults = {
|
||||||
|
|
|
@ -7,22 +7,25 @@
|
||||||
|
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import angular from 'angular';
|
|
||||||
|
|
||||||
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
||||||
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { createJobForSaving } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function MultiMetricJobServiceProvider(
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlMultiMetricJobService', function (
|
|
||||||
$q,
|
$q,
|
||||||
es,
|
es,
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private) {
|
||||||
mlFieldFormatService,
|
|
||||||
mlJobService) {
|
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const fieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
|
|
||||||
|
class MultiMetricJobService {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
job: {
|
job: {
|
||||||
swimlane: [],
|
swimlane: [],
|
||||||
|
@ -39,8 +42,9 @@ module.service('mlMultiMetricJobService', function (
|
||||||
totalResults: 0
|
totalResults: 0
|
||||||
};
|
};
|
||||||
this.job = {};
|
this.job = {};
|
||||||
|
}
|
||||||
|
|
||||||
this.clearChartData = function () {
|
clearChartData() {
|
||||||
this.chartData.job.swimlane = [];
|
this.chartData.job.swimlane = [];
|
||||||
this.chartData.job.line = [];
|
this.chartData.job.line = [];
|
||||||
this.chartData.job.bars = [];
|
this.chartData.job.bars = [];
|
||||||
|
@ -49,12 +53,11 @@ module.service('mlMultiMetricJobService', function (
|
||||||
this.chartData.loadingDifference = 0;
|
this.chartData.loadingDifference = 0;
|
||||||
this.chartData.eventRateHighestValue = 0;
|
this.chartData.eventRateHighestValue = 0;
|
||||||
this.chartData.totalResults = 0;
|
this.chartData.totalResults = 0;
|
||||||
|
|
||||||
this.job = {};
|
this.job = {};
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getLineChartResults = function (formConfig, thisLoadTimestamp) {
|
getLineChartResults(formConfig, thisLoadTimestamp) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
const fieldIds = Object.keys(formConfig.fields).sort();
|
const fieldIds = Object.keys(formConfig.fields).sort();
|
||||||
|
|
||||||
|
@ -91,7 +94,7 @@ module.service('mlMultiMetricJobService', function (
|
||||||
if (fieldId !== EVENT_RATE_COUNT_FIELD) {
|
if (fieldId !== EVENT_RATE_COUNT_FIELD) {
|
||||||
const field = formConfig.fields[fieldId];
|
const field = formConfig.fields[fieldId];
|
||||||
const aggType = field.agg.type.dslName;
|
const aggType = field.agg.type.dslName;
|
||||||
this.chartData.detectors[fieldId].fieldFormat = mlFieldFormatService.getFieldFormatFromIndexPattern(
|
this.chartData.detectors[fieldId].fieldFormat = fieldFormatService.getFieldFormatFromIndexPattern(
|
||||||
formConfig.indexPattern,
|
formConfig.indexPattern,
|
||||||
fieldId,
|
fieldId,
|
||||||
aggType);
|
aggType);
|
||||||
|
@ -99,7 +102,7 @@ module.service('mlMultiMetricJobService', function (
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
}
|
}
|
||||||
const aggregationsByTime = _.get(resp, ['aggregations', 'times', 'buckets'], []);
|
const aggregationsByTime = _.get(resp, ['aggregations', 'times', 'buckets'], []);
|
||||||
|
|
||||||
|
@ -163,87 +166,16 @@ module.service('mlMultiMetricJobService', function (
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
function getSearchJsonFromConfig(formConfig) {
|
|
||||||
const interval = formConfig.chartInterval.getInterval().asMilliseconds() + 'ms';
|
|
||||||
// clone the query as we're modifying it
|
|
||||||
const query = _.cloneDeep(formConfig.combinedQuery);
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
'index': formConfig.indexPattern.title,
|
|
||||||
'size': 0,
|
|
||||||
'body': {
|
|
||||||
'query': {},
|
|
||||||
'aggs': {
|
|
||||||
'times': {
|
|
||||||
'date_histogram': {
|
|
||||||
'field': formConfig.timeField,
|
|
||||||
'interval': interval,
|
|
||||||
'min_doc_count': 0,
|
|
||||||
'extended_bounds': {
|
|
||||||
'min': formConfig.start,
|
|
||||||
'max': formConfig.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
query.bool.must.push({
|
|
||||||
'range': {
|
|
||||||
[formConfig.timeField]: {
|
|
||||||
'gte': formConfig.start,
|
|
||||||
'lte': formConfig.end,
|
|
||||||
'format': formConfig.format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// if the data is partitioned, add an additional search term
|
|
||||||
if (formConfig.firstSplitFieldName !== undefined) {
|
|
||||||
query.bool.must.push({
|
|
||||||
term: {
|
|
||||||
[formConfig.splitField.name]: formConfig.firstSplitFieldName
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
json.body.query = query;
|
getJobFromConfig(formConfig) {
|
||||||
|
|
||||||
if (Object.keys(formConfig.fields).length) {
|
|
||||||
json.body.aggs.times.aggs = {};
|
|
||||||
_.each(formConfig.fields, (field) => {
|
|
||||||
if (field.id !== EVENT_RATE_COUNT_FIELD) {
|
|
||||||
json.body.aggs.times.aggs[field.id] = {
|
|
||||||
[field.agg.type.dslName]: { field: field.name }
|
|
||||||
};
|
|
||||||
|
|
||||||
if (field.agg.type.dslName === 'percentiles') {
|
|
||||||
json.body.aggs.times.aggs[field.id][field.agg.type.dslName].percents = [ML_MEDIAN_PERCENTS];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createJobForSaving(job) {
|
|
||||||
const newJob = angular.copy(job);
|
|
||||||
delete newJob.datafeed_config;
|
|
||||||
return newJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getJobFromConfig = function (formConfig) {
|
|
||||||
const job = mlJobService.getBlankJob();
|
const job = mlJobService.getBlankJob();
|
||||||
job.data_description.time_field = formConfig.timeField;
|
job.data_description.time_field = formConfig.timeField;
|
||||||
|
|
||||||
|
@ -297,12 +229,10 @@ module.service('mlMultiMetricJobService', function (
|
||||||
delete job.data_description.format;
|
delete job.data_description.format;
|
||||||
|
|
||||||
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
||||||
|
|
||||||
job.datafeed_config = {
|
job.datafeed_config = {
|
||||||
query,
|
query,
|
||||||
indices
|
indices
|
||||||
};
|
};
|
||||||
|
|
||||||
job.job_id = formConfig.jobId;
|
job.job_id = formConfig.jobId;
|
||||||
job.description = formConfig.description;
|
job.description = formConfig.description;
|
||||||
job.groups = formConfig.jobGroups;
|
job.groups = formConfig.jobGroups;
|
||||||
|
@ -312,10 +242,10 @@ module.service('mlMultiMetricJobService', function (
|
||||||
}
|
}
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.createJob = function (formConfig) {
|
createJob(formConfig) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
this.job = this.getJobFromConfig(formConfig);
|
this.job = this.getJobFromConfig(formConfig);
|
||||||
const job = createJobForSaving(this.job);
|
const job = createJobForSaving(this.job);
|
||||||
|
@ -324,23 +254,92 @@ module.service('mlMultiMetricJobService', function (
|
||||||
mlJobService.saveNewJob(job)
|
mlJobService.saveNewJob(job)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
deferred.resolve(this.job);
|
resolve(this.job);
|
||||||
} else {
|
} else {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.startDatafeed = function (formConfig) {
|
startDatafeed(formConfig) {
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.stopDatafeed = function (formConfig) {
|
stopDatafeed(formConfig) {
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return new MultiMetricJobService();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchJsonFromConfig(formConfig) {
|
||||||
|
const interval = formConfig.chartInterval.getInterval().asMilliseconds() + 'ms';
|
||||||
|
// clone the query as we're modifying it
|
||||||
|
const query = _.cloneDeep(formConfig.combinedQuery);
|
||||||
|
|
||||||
|
const json = {
|
||||||
|
index: formConfig.indexPattern.title,
|
||||||
|
size: 0,
|
||||||
|
body: {
|
||||||
|
query: {},
|
||||||
|
aggs: {
|
||||||
|
times: {
|
||||||
|
date_histogram: {
|
||||||
|
field: formConfig.timeField,
|
||||||
|
interval: interval,
|
||||||
|
min_doc_count: 0,
|
||||||
|
extended_bounds: {
|
||||||
|
min: formConfig.start,
|
||||||
|
max: formConfig.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
query.bool.must.push({
|
||||||
|
range: {
|
||||||
|
[formConfig.timeField]: {
|
||||||
|
gte: formConfig.start,
|
||||||
|
lte: formConfig.end,
|
||||||
|
format: formConfig.format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// if the data is partitioned, add an additional search term
|
||||||
|
if (formConfig.firstSplitFieldName !== undefined) {
|
||||||
|
query.bool.must.push({
|
||||||
|
term: {
|
||||||
|
[formConfig.splitField.name]: formConfig.firstSplitFieldName
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
json.body.query = query;
|
||||||
|
|
||||||
|
if (Object.keys(formConfig.fields).length) {
|
||||||
|
json.body.aggs.times.aggs = {};
|
||||||
|
_.each(formConfig.fields, (field) => {
|
||||||
|
if (field.id !== EVENT_RATE_COUNT_FIELD) {
|
||||||
|
json.body.aggs.times.aggs[field.id] = {
|
||||||
|
[field.agg.type.dslName]: { field: field.name }
|
||||||
|
};
|
||||||
|
|
||||||
|
if (field.agg.type.dslName === 'percentiles') {
|
||||||
|
json.body.aggs.times.aggs[field.id][field.agg.type.dslName].percents = [ML_MEDIAN_PERCENTS];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ import {
|
||||||
createResultsUrl,
|
createResultsUrl,
|
||||||
addNewJobToRecentlyAccessed,
|
addNewJobToRecentlyAccessed,
|
||||||
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { PopulationJobServiceProvider } from './create_job_service';
|
||||||
|
import { FullTimeRangeSelectorServiceProvider } from 'plugins/ml/components/full_time_range_selector/full_time_range_selector_service';
|
||||||
import template from './create_job.html';
|
import template from './create_job.html';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
|
@ -62,10 +65,7 @@ module
|
||||||
$q,
|
$q,
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private,
|
||||||
mlJobService,
|
|
||||||
mlPopulationJobService,
|
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlFullTimeRangeSelectorService,
|
|
||||||
AppState) {
|
AppState) {
|
||||||
|
|
||||||
timefilter.enableTimeRangeSelector();
|
timefilter.enableTimeRangeSelector();
|
||||||
|
@ -74,6 +74,9 @@ module
|
||||||
const MlTimeBuckets = Private(IntervalHelperProvider);
|
const MlTimeBuckets = Private(IntervalHelperProvider);
|
||||||
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
||||||
const chartDataUtils = Private(ChartDataUtilsProvider);
|
const chartDataUtils = Private(ChartDataUtilsProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlPopulationJobService = Private(PopulationJobServiceProvider);
|
||||||
|
const mlFullTimeRangeSelectorService = Private(FullTimeRangeSelectorServiceProvider);
|
||||||
$scope.addNewJobToRecentlyAccessed = addNewJobToRecentlyAccessed;
|
$scope.addNewJobToRecentlyAccessed = addNewJobToRecentlyAccessed;
|
||||||
|
|
||||||
const stateDefaults = {
|
const stateDefaults = {
|
||||||
|
|
|
@ -7,26 +7,29 @@
|
||||||
|
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import angular from 'angular';
|
|
||||||
|
|
||||||
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
import { EVENT_RATE_COUNT_FIELD } from 'plugins/ml/jobs/new_job/simple/components/constants/general';
|
||||||
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
import { IntervalHelperProvider } from 'plugins/ml/util/ml_time_buckets';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { createJobForSaving } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlPopulationJobService', function (
|
export function PopulationJobServiceProvider(
|
||||||
$q,
|
$q,
|
||||||
es,
|
es,
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private) {
|
||||||
mlFieldFormatService,
|
|
||||||
mlJobService) {
|
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
const TimeBuckets = Private(IntervalHelperProvider);
|
const TimeBuckets = Private(IntervalHelperProvider);
|
||||||
|
const fieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
const OVER_FIELD_EXAMPLES_COUNT = 40;
|
const OVER_FIELD_EXAMPLES_COUNT = 40;
|
||||||
|
|
||||||
|
class PopulationJobService {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
job: {
|
job: {
|
||||||
swimlane: [],
|
swimlane: [],
|
||||||
|
@ -43,8 +46,9 @@ module.service('mlPopulationJobService', function (
|
||||||
totalResults: 0
|
totalResults: 0
|
||||||
};
|
};
|
||||||
this.job = {};
|
this.job = {};
|
||||||
|
}
|
||||||
|
|
||||||
this.clearChartData = function () {
|
clearChartData() {
|
||||||
this.chartData.job.swimlane = [];
|
this.chartData.job.swimlane = [];
|
||||||
this.chartData.job.line = [];
|
this.chartData.job.line = [];
|
||||||
this.chartData.job.bars = [];
|
this.chartData.job.bars = [];
|
||||||
|
@ -53,12 +57,11 @@ module.service('mlPopulationJobService', function (
|
||||||
this.chartData.loadingDifference = 0;
|
this.chartData.loadingDifference = 0;
|
||||||
this.chartData.eventRateHighestValue = 0;
|
this.chartData.eventRateHighestValue = 0;
|
||||||
this.chartData.totalResults = 0;
|
this.chartData.totalResults = 0;
|
||||||
|
|
||||||
this.job = {};
|
this.job = {};
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getLineChartResults = function (formConfig, thisLoadTimestamp) {
|
getLineChartResults(formConfig, thisLoadTimestamp) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
const fieldIds = formConfig.fields.map(f => f.id);
|
const fieldIds = formConfig.fields.map(f => f.id);
|
||||||
|
|
||||||
|
@ -79,7 +82,7 @@ module.service('mlPopulationJobService', function (
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
const searchJson = getSearchJsonFromConfig(formConfig);
|
const searchJson = getSearchJsonFromConfig(formConfig, timefilter, TimeBuckets);
|
||||||
|
|
||||||
es.search(searchJson)
|
es.search(searchJson)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
@ -96,7 +99,7 @@ module.service('mlPopulationJobService', function (
|
||||||
if (fieldId !== EVENT_RATE_COUNT_FIELD) {
|
if (fieldId !== EVENT_RATE_COUNT_FIELD) {
|
||||||
const field = formConfig.fields[i];
|
const field = formConfig.fields[i];
|
||||||
const aggType = field.agg.type.dslName;
|
const aggType = field.agg.type.dslName;
|
||||||
this.chartData.detectors[i].fieldFormat = mlFieldFormatService.getFieldFormatFromIndexPattern(
|
this.chartData.detectors[i].fieldFormat = fieldFormatService.getFieldFormatFromIndexPattern(
|
||||||
formConfig.indexPattern,
|
formConfig.indexPattern,
|
||||||
fieldId,
|
fieldId,
|
||||||
aggType);
|
aggType);
|
||||||
|
@ -104,7 +107,7 @@ module.service('mlPopulationJobService', function (
|
||||||
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
}
|
}
|
||||||
const aggregationsByTime = _.get(resp, ['aggregations', 'times', 'buckets'], []);
|
const aggregationsByTime = _.get(resp, ['aggregations', 'times', 'buckets'], []);
|
||||||
|
|
||||||
|
@ -186,14 +189,122 @@ module.service('mlPopulationJobService', function (
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getJobFromConfig(formConfig) {
|
||||||
|
const job = mlJobService.getBlankJob();
|
||||||
|
job.data_description.time_field = formConfig.timeField;
|
||||||
|
|
||||||
|
formConfig.fields.forEach(field => {
|
||||||
|
let func = field.agg.type.mlName;
|
||||||
|
if (formConfig.isSparseData) {
|
||||||
|
if (field.agg.type.dslName === 'count') {
|
||||||
|
func = func.replace(/count/, 'non_zero_count');
|
||||||
|
} else if(field.agg.type.dslName === 'sum') {
|
||||||
|
func = func.replace(/sum/, 'non_null_sum');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const dtr = {
|
||||||
|
function: func
|
||||||
|
};
|
||||||
|
|
||||||
|
dtr.detector_description = func;
|
||||||
|
|
||||||
|
if (field.id !== EVENT_RATE_COUNT_FIELD) {
|
||||||
|
dtr.field_name = field.name;
|
||||||
|
dtr.detector_description += `(${field.name})`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field.splitField !== undefined) {
|
||||||
|
dtr.by_field_name = field.splitField.name;
|
||||||
|
dtr.detector_description += ` by ${dtr.by_field_name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (formConfig.overField !== undefined) {
|
||||||
|
dtr.over_field_name = formConfig.overField.name;
|
||||||
|
dtr.detector_description += ` over ${dtr.over_field_name}`;
|
||||||
|
}
|
||||||
|
// if (formConfig.splitField !== undefined) {
|
||||||
|
// dtr.partition_field_name = formConfig.splitField;
|
||||||
|
// }
|
||||||
|
job.analysis_config.detectors.push(dtr);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
const influencerFields = formConfig.influencerFields.map(f => f.name);
|
||||||
|
if (influencerFields && influencerFields.length) {
|
||||||
|
job.analysis_config.influencers = influencerFields;
|
||||||
|
}
|
||||||
|
|
||||||
|
let query = {
|
||||||
|
match_all: {}
|
||||||
};
|
};
|
||||||
|
if (formConfig.query.query_string.query !== '*' || formConfig.filters.length) {
|
||||||
|
query = formConfig.combinedQuery;
|
||||||
|
}
|
||||||
|
|
||||||
|
job.analysis_config.bucket_span = formConfig.bucketSpan;
|
||||||
|
|
||||||
|
job.analysis_limits = {
|
||||||
|
model_memory_limit: formConfig.modelMemoryLimit
|
||||||
|
};
|
||||||
|
|
||||||
|
delete job.data_description.field_delimiter;
|
||||||
|
delete job.data_description.quote_character;
|
||||||
|
delete job.data_description.time_format;
|
||||||
|
delete job.data_description.format;
|
||||||
|
|
||||||
|
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
||||||
|
|
||||||
|
job.datafeed_config = {
|
||||||
|
query,
|
||||||
|
indices,
|
||||||
|
};
|
||||||
|
job.job_id = formConfig.jobId;
|
||||||
|
job.description = formConfig.description;
|
||||||
|
job.groups = formConfig.jobGroups;
|
||||||
|
|
||||||
|
if (formConfig.useDedicatedIndex) {
|
||||||
|
job.results_index_name = job.job_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
return job;
|
||||||
|
}
|
||||||
|
|
||||||
|
createJob(formConfig) {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
|
this.job = this.getJobFromConfig(formConfig);
|
||||||
|
const job = createJobForSaving(this.job);
|
||||||
|
|
||||||
|
// DO THE SAVE
|
||||||
|
mlJobService.saveNewJob(job)
|
||||||
|
.then((resp) => {
|
||||||
|
if (resp.success) {
|
||||||
|
resolve(this.job);
|
||||||
|
} else {
|
||||||
|
reject(resp);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
startDatafeed(formConfig) {
|
||||||
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
|
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
||||||
|
}
|
||||||
|
|
||||||
|
stopDatafeed(formConfig) {
|
||||||
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
|
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getSearchJsonFromConfig(formConfig) {
|
function getSearchJsonFromConfig(formConfig) {
|
||||||
const bounds = timefilter.getActiveBounds();
|
const bounds = timefilter.getActiveBounds();
|
||||||
|
@ -207,19 +318,19 @@ module.service('mlPopulationJobService', function (
|
||||||
const query = _.cloneDeep(formConfig.combinedQuery);
|
const query = _.cloneDeep(formConfig.combinedQuery);
|
||||||
|
|
||||||
const json = {
|
const json = {
|
||||||
'index': formConfig.indexPattern.title,
|
index: formConfig.indexPattern.title,
|
||||||
'size': 0,
|
size: 0,
|
||||||
'body': {
|
body: {
|
||||||
'query': {},
|
query: {},
|
||||||
'aggs': {
|
aggs: {
|
||||||
'times': {
|
times: {
|
||||||
'date_histogram': {
|
date_histogram: {
|
||||||
'field': formConfig.timeField,
|
field: formConfig.timeField,
|
||||||
'interval': interval,
|
interval: interval,
|
||||||
'min_doc_count': 0,
|
min_doc_count: 0,
|
||||||
'extended_bounds': {
|
extended_bounds: {
|
||||||
'min': formConfig.start,
|
min: formConfig.start,
|
||||||
'max': formConfig.end,
|
max: formConfig.end,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -228,11 +339,11 @@ module.service('mlPopulationJobService', function (
|
||||||
};
|
};
|
||||||
|
|
||||||
query.bool.must.push({
|
query.bool.must.push({
|
||||||
'range': {
|
range: {
|
||||||
[formConfig.timeField]: {
|
[formConfig.timeField]: {
|
||||||
'gte': formConfig.start,
|
gte: formConfig.start,
|
||||||
'lte': formConfig.end,
|
lte: formConfig.end,
|
||||||
'format': formConfig.format
|
format: formConfig.format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -319,118 +430,5 @@ module.service('mlPopulationJobService', function (
|
||||||
return json;
|
return json;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createJobForSaving(job) {
|
return new PopulationJobService();
|
||||||
const newJob = angular.copy(job);
|
}
|
||||||
delete newJob.datafeed_config;
|
|
||||||
return newJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getJobFromConfig = function (formConfig) {
|
|
||||||
const job = mlJobService.getBlankJob();
|
|
||||||
job.data_description.time_field = formConfig.timeField;
|
|
||||||
|
|
||||||
formConfig.fields.forEach(field => {
|
|
||||||
let func = field.agg.type.mlName;
|
|
||||||
if (formConfig.isSparseData) {
|
|
||||||
if (field.agg.type.dslName === 'count') {
|
|
||||||
func = func.replace(/count/, 'non_zero_count');
|
|
||||||
} else if(field.agg.type.dslName === 'sum') {
|
|
||||||
func = func.replace(/sum/, 'non_null_sum');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
const dtr = {
|
|
||||||
function: func
|
|
||||||
};
|
|
||||||
|
|
||||||
dtr.detector_description = func;
|
|
||||||
|
|
||||||
if (field.id !== EVENT_RATE_COUNT_FIELD) {
|
|
||||||
dtr.field_name = field.name;
|
|
||||||
dtr.detector_description += `(${field.name})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (field.splitField !== undefined) {
|
|
||||||
dtr.by_field_name = field.splitField.name;
|
|
||||||
dtr.detector_description += ` by ${dtr.by_field_name}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formConfig.overField !== undefined) {
|
|
||||||
dtr.over_field_name = formConfig.overField.name;
|
|
||||||
dtr.detector_description += ` over ${dtr.over_field_name}`;
|
|
||||||
}
|
|
||||||
// if (formConfig.splitField !== undefined) {
|
|
||||||
// dtr.partition_field_name = formConfig.splitField;
|
|
||||||
// }
|
|
||||||
job.analysis_config.detectors.push(dtr);
|
|
||||||
});
|
|
||||||
|
|
||||||
const influencerFields = formConfig.influencerFields.map(f => f.name);
|
|
||||||
if (influencerFields && influencerFields.length) {
|
|
||||||
job.analysis_config.influencers = influencerFields;
|
|
||||||
}
|
|
||||||
|
|
||||||
let query = {
|
|
||||||
match_all: {}
|
|
||||||
};
|
|
||||||
if (formConfig.query.query_string.query !== '*' || formConfig.filters.length) {
|
|
||||||
query = formConfig.combinedQuery;
|
|
||||||
}
|
|
||||||
|
|
||||||
job.analysis_config.bucket_span = formConfig.bucketSpan;
|
|
||||||
|
|
||||||
job.analysis_limits = {
|
|
||||||
model_memory_limit: formConfig.modelMemoryLimit
|
|
||||||
};
|
|
||||||
|
|
||||||
delete job.data_description.field_delimiter;
|
|
||||||
delete job.data_description.quote_character;
|
|
||||||
delete job.data_description.time_format;
|
|
||||||
delete job.data_description.format;
|
|
||||||
|
|
||||||
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
|
||||||
|
|
||||||
job.datafeed_config = {
|
|
||||||
query,
|
|
||||||
indices
|
|
||||||
};
|
|
||||||
|
|
||||||
job.job_id = formConfig.jobId;
|
|
||||||
job.description = formConfig.description;
|
|
||||||
job.groups = formConfig.jobGroups;
|
|
||||||
|
|
||||||
if (formConfig.useDedicatedIndex) {
|
|
||||||
job.results_index_name = job.job_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
return job;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.createJob = function (formConfig) {
|
|
||||||
const deferred = $q.defer();
|
|
||||||
|
|
||||||
this.job = this.getJobFromConfig(formConfig);
|
|
||||||
const job = createJobForSaving(this.job);
|
|
||||||
|
|
||||||
// DO THE SAVE
|
|
||||||
mlJobService.saveNewJob(job)
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.success) {
|
|
||||||
deferred.resolve(this.job);
|
|
||||||
} else {
|
|
||||||
deferred.reject(resp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.startDatafeed = function (formConfig) {
|
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
|
||||||
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.stopDatafeed = function (formConfig) {
|
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
|
||||||
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
|
@ -19,6 +19,9 @@ import { checkLicenseExpired } from 'plugins/ml/license/check_license';
|
||||||
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
import { checkCreateJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
||||||
import { getIndexPatternWithRoute, getSavedSearchWithRoute } from 'plugins/ml/util/index_utils';
|
import { getIndexPatternWithRoute, getSavedSearchWithRoute } from 'plugins/ml/util/index_utils';
|
||||||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CreateRecognizerJobsServiceProvider } from './create_job_service';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
import template from './create_job.html';
|
import template from './create_job.html';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
|
@ -42,13 +45,12 @@ module
|
||||||
$window,
|
$window,
|
||||||
$route,
|
$route,
|
||||||
$q,
|
$q,
|
||||||
ml,
|
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private,
|
||||||
mlCreateRecognizerJobsService,
|
|
||||||
mlJobService,
|
|
||||||
mlMessageBarService) {
|
mlMessageBarService) {
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlCreateRecognizerJobsService = Private(CreateRecognizerJobsServiceProvider);
|
||||||
timefilter.disableTimeRangeSelector();
|
timefilter.disableTimeRangeSelector();
|
||||||
timefilter.disableAutoRefreshSelector();
|
timefilter.disableAutoRefreshSelector();
|
||||||
$scope.tt = timefilter;
|
$scope.tt = timefilter;
|
||||||
|
@ -145,7 +147,7 @@ module
|
||||||
function loadJobConfigs() {
|
function loadJobConfigs() {
|
||||||
// load the job and datafeed configs as well as the kibana saved objects
|
// load the job and datafeed configs as well as the kibana saved objects
|
||||||
// from the recognizer endpoint
|
// from the recognizer endpoint
|
||||||
ml.getDataRecognizerModule({ moduleId })
|
$q.when(ml.getDataRecognizerModule({ moduleId }))
|
||||||
.then(resp => {
|
.then(resp => {
|
||||||
// populate the jobs and datafeeds
|
// populate the jobs and datafeeds
|
||||||
if (resp.jobs && resp.jobs.length) {
|
if (resp.jobs && resp.jobs.length) {
|
||||||
|
@ -259,7 +261,7 @@ module
|
||||||
const tempQuery = (savedSearch.id === undefined) ?
|
const tempQuery = (savedSearch.id === undefined) ?
|
||||||
undefined : combinedQuery;
|
undefined : combinedQuery;
|
||||||
|
|
||||||
ml.setupDataRecognizerConfig({ moduleId, prefix, groups, query: tempQuery, indexPatternName })
|
$q.when(ml.setupDataRecognizerConfig({ moduleId, prefix, groups, query: tempQuery, indexPatternName }))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.jobs) {
|
if (resp.jobs) {
|
||||||
$scope.formConfig.jobs.forEach((job) => {
|
$scope.formConfig.jobs.forEach((job) => {
|
||||||
|
|
|
@ -6,44 +6,20 @@
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import angular from 'angular';
|
|
||||||
|
|
||||||
import { getQueryFromSavedSearch } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
import { getQueryFromSavedSearch } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function CreateRecognizerJobsServiceProvider(Private, $q) {
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlCreateRecognizerJobsService', function (
|
|
||||||
es,
|
|
||||||
Private,
|
|
||||||
$http,
|
|
||||||
$q,
|
|
||||||
chrome,
|
|
||||||
mlJobService) {
|
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
const savedObjectsClient = Private(SavedObjectsClientProvider);
|
const savedObjectsClient = Private(SavedObjectsClientProvider);
|
||||||
|
class CreateRecognizerJobsService {
|
||||||
|
|
||||||
this.createJob = function (job, formConfig) {
|
constructor() {}
|
||||||
return $q((resolve, reject) => {
|
|
||||||
const newJob = angular.copy(job.jobConfig);
|
|
||||||
const jobId = formConfig.jobLabel + job.id;
|
|
||||||
newJob.job_id = jobId;
|
|
||||||
newJob.groups = formConfig.jobGroups;
|
|
||||||
|
|
||||||
mlJobService.saveNewJob(newJob)
|
createDatafeed(job, formConfig) {
|
||||||
.then((resp) => {
|
|
||||||
if (resp.success) {
|
|
||||||
resolve(resp);
|
|
||||||
} else {
|
|
||||||
reject(resp);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.createDatafeed = function (job, formConfig) {
|
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const jobId = formConfig.jobLabel + job.id;
|
const jobId = formConfig.jobLabel + job.id;
|
||||||
|
|
||||||
|
@ -55,77 +31,24 @@ module.service('mlCreateRecognizerJobsService', function (
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.startDatafeed = function (datafeedId, jobId, start, end) {
|
startDatafeed(datafeedId, jobId, start, end) {
|
||||||
return mlJobService.startDatafeed(datafeedId, jobId, start, end);
|
return mlJobService.startDatafeed(datafeedId, jobId, start, end);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.stopDatafeed = function (formConfig) {
|
loadExistingSavedObjects(type) {
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
|
||||||
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.checkDatafeedStatus = function (formConfig) {
|
|
||||||
return mlJobService.updateSingleJobDatafeedState(formConfig.jobId);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadExistingSavedObjects = function (type) {
|
|
||||||
return savedObjectsClient.find({ type, perPage: 1000 });
|
return savedObjectsClient.find({ type, perPage: 1000 });
|
||||||
};
|
}
|
||||||
|
|
||||||
this.createSavedObject = function (type, obj) {
|
indexTimeRange(indexPattern, formConfig) {
|
||||||
return savedObjectsClient.create(type, obj);
|
|
||||||
};
|
|
||||||
|
|
||||||
this.createSavedObjectWithId = function (type, id, obj) {
|
|
||||||
const basePath = chrome.addBasePath('/api/saved_objects');
|
|
||||||
const url = `${basePath}/${type}/${id}`;
|
|
||||||
|
|
||||||
return $http.post(url, { attributes: obj })
|
|
||||||
.catch(e => {
|
|
||||||
throw e.data.message;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
this.indexTimeRange = function (indexPattern, formConfig) {
|
|
||||||
return $q((resolve, reject) => {
|
|
||||||
const obj = { success: true, start: { epoch: 0, string: '' }, end: { epoch: 0, string: '' } };
|
|
||||||
const query = getQueryFromSavedSearch(formConfig);
|
const query = getQueryFromSavedSearch(formConfig);
|
||||||
|
return ml.getTimeFieldRange({
|
||||||
es.search({
|
|
||||||
index: indexPattern.title,
|
index: indexPattern.title,
|
||||||
size: 0,
|
timeFieldName: indexPattern.timeFieldName,
|
||||||
body: {
|
query
|
||||||
query,
|
|
||||||
aggs: {
|
|
||||||
earliest: {
|
|
||||||
min: {
|
|
||||||
field: indexPattern.timeFieldName
|
|
||||||
}
|
|
||||||
},
|
|
||||||
latest: {
|
|
||||||
max: {
|
|
||||||
field: indexPattern.timeFieldName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.aggregations && resp.aggregations.earliest && resp.aggregations.latest) {
|
|
||||||
obj.start.epoch = resp.aggregations.earliest.value;
|
|
||||||
obj.start.string = resp.aggregations.earliest.value_as_string;
|
|
||||||
|
|
||||||
obj.end.epoch = resp.aggregations.latest.value;
|
|
||||||
obj.end.string = resp.aggregations.latest.value_as_string;
|
|
||||||
}
|
|
||||||
resolve(obj);
|
|
||||||
})
|
|
||||||
.catch((resp) => {
|
|
||||||
reject(resp);
|
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
};
|
}
|
||||||
|
return new CreateRecognizerJobsService();
|
||||||
});
|
}
|
||||||
|
|
|
@ -36,6 +36,9 @@ import {
|
||||||
createResultsUrl,
|
createResultsUrl,
|
||||||
addNewJobToRecentlyAccessed,
|
addNewJobToRecentlyAccessed,
|
||||||
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
moveToAdvancedJobCreationProvider } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { SingleMetricJobServiceProvider } from './create_job_service';
|
||||||
|
import { FullTimeRangeSelectorServiceProvider } from 'plugins/ml/components/full_time_range_selector/full_time_range_selector_service';
|
||||||
|
|
||||||
import template from './create_job.html';
|
import template from './create_job.html';
|
||||||
|
|
||||||
|
@ -63,10 +66,7 @@ module
|
||||||
$q,
|
$q,
|
||||||
timefilter,
|
timefilter,
|
||||||
Private,
|
Private,
|
||||||
mlJobService,
|
|
||||||
mlSingleMetricJobService,
|
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlFullTimeRangeSelectorService,
|
|
||||||
AppState) {
|
AppState) {
|
||||||
|
|
||||||
timefilter.enableTimeRangeSelector();
|
timefilter.enableTimeRangeSelector();
|
||||||
|
@ -74,6 +74,9 @@ module
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
const MlTimeBuckets = Private(IntervalHelperProvider);
|
const MlTimeBuckets = Private(IntervalHelperProvider);
|
||||||
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
const moveToAdvancedJobCreation = Private(moveToAdvancedJobCreationProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlSingleMetricJobService = Private(SingleMetricJobServiceProvider);
|
||||||
|
const mlFullTimeRangeSelectorService = Private(FullTimeRangeSelectorServiceProvider);
|
||||||
|
|
||||||
const stateDefaults = {
|
const stateDefaults = {
|
||||||
mlJobSettings: {}
|
mlJobSettings: {}
|
||||||
|
|
|
@ -7,26 +7,28 @@
|
||||||
|
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
import angular from 'angular';
|
|
||||||
import 'ui/timefilter';
|
|
||||||
|
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
|
|
||||||
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
import { calculateTextWidth } from 'plugins/ml/util/string_utils';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
import { createJobForSaving } from 'plugins/ml/jobs/new_job/utils/new_job_utils';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function SingleMetricJobServiceProvider(
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlSingleMetricJobService', function (
|
|
||||||
$q,
|
$q,
|
||||||
es,
|
es,
|
||||||
timefilter,
|
Private) {
|
||||||
Private,
|
|
||||||
mlFieldFormatService,
|
|
||||||
mlJobService,
|
|
||||||
mlResultsService) {
|
|
||||||
|
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const fieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
|
|
||||||
|
class SingleMetricJobService {
|
||||||
|
|
||||||
|
constructor() {
|
||||||
this.chartData = {
|
this.chartData = {
|
||||||
line: [],
|
line: [],
|
||||||
model: [],
|
model: [],
|
||||||
|
@ -38,9 +40,10 @@ module.service('mlSingleMetricJobService', function (
|
||||||
totalResults: 0
|
totalResults: 0
|
||||||
};
|
};
|
||||||
this.job = {};
|
this.job = {};
|
||||||
|
}
|
||||||
|
|
||||||
this.getLineChartResults = function (formConfig) {
|
getLineChartResults(formConfig) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
this.chartData.line = [];
|
this.chartData.line = [];
|
||||||
this.chartData.model = [];
|
this.chartData.model = [];
|
||||||
|
@ -53,7 +56,7 @@ module.service('mlSingleMetricJobService', function (
|
||||||
|
|
||||||
const aggType = formConfig.agg.type.dslName;
|
const aggType = formConfig.agg.type.dslName;
|
||||||
if (formConfig.field && formConfig.field.id) {
|
if (formConfig.field && formConfig.field.id) {
|
||||||
this.chartData.fieldFormat = mlFieldFormatService.getFieldFormatFromIndexPattern(
|
this.chartData.fieldFormat = fieldFormatService.getFieldFormatFromIndexPattern(
|
||||||
formConfig.indexPattern,
|
formConfig.indexPattern,
|
||||||
formConfig.field.id,
|
formConfig.field.id,
|
||||||
aggType);
|
aggType);
|
||||||
|
@ -109,107 +112,16 @@ module.service('mlSingleMetricJobService', function (
|
||||||
this.chartData.chartTicksMargin.width = calculateTextWidth(this.chartData.highestValue, true) + 10;
|
this.chartData.chartTicksMargin.width = calculateTextWidth(this.chartData.highestValue, true) + 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
function processLineChartResults(data, scale = 1) {
|
|
||||||
const lineData = [];
|
|
||||||
_.each(data, (dataForTime, t) => {
|
|
||||||
const time = +t;
|
|
||||||
const date = new Date(time);
|
|
||||||
lineData.push({
|
|
||||||
date: date,
|
|
||||||
time: time,
|
|
||||||
lower: (dataForTime.modelLower * scale),
|
|
||||||
value: dataForTime.actual,
|
|
||||||
upper: (dataForTime.modelUpper * scale)
|
|
||||||
});
|
});
|
||||||
});
|
|
||||||
|
|
||||||
return _.sortBy(lineData, 'time');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function processSwimlaneResults(bucketScoreData, init) {
|
getJobFromConfig(formConfig) {
|
||||||
// create a dataset in format used by the model plot chart.
|
|
||||||
// create empty swimlane dataset
|
|
||||||
// i.e. array of Objects with keys date (JavaScript date), value, lower and upper.
|
|
||||||
const swimlaneData = [];
|
|
||||||
_.each(bucketScoreData, (value, t) => {
|
|
||||||
const time = +t;
|
|
||||||
const date = new Date(time);
|
|
||||||
value = init ? 0 : value;
|
|
||||||
swimlaneData.push({
|
|
||||||
date,
|
|
||||||
time,
|
|
||||||
value,
|
|
||||||
color: ''
|
|
||||||
});
|
|
||||||
});
|
|
||||||
return swimlaneData;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getSearchJsonFromConfig(formConfig) {
|
|
||||||
const interval = formConfig.chartInterval.getInterval().asMilliseconds() + 'ms';
|
|
||||||
// clone the query as we're modifying it
|
|
||||||
const query = _.cloneDeep(formConfig.combinedQuery);
|
|
||||||
|
|
||||||
const json = {
|
|
||||||
'index': formConfig.indexPattern.title,
|
|
||||||
'size': 0,
|
|
||||||
'body': {
|
|
||||||
'query': {},
|
|
||||||
'aggs': {
|
|
||||||
'times': {
|
|
||||||
'date_histogram': {
|
|
||||||
'field': formConfig.timeField,
|
|
||||||
'interval': interval,
|
|
||||||
'min_doc_count': 0,
|
|
||||||
'extended_bounds': {
|
|
||||||
'min': formConfig.start,
|
|
||||||
'max': formConfig.end,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
query.bool.must.push({
|
|
||||||
'range': {
|
|
||||||
[formConfig.timeField]: {
|
|
||||||
'gte': formConfig.start,
|
|
||||||
'lte': formConfig.end,
|
|
||||||
'format': formConfig.format
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
json.body.query = query;
|
|
||||||
|
|
||||||
if (formConfig.field !== null) {
|
|
||||||
json.body.aggs.times.aggs = {
|
|
||||||
'field_value': {
|
|
||||||
[formConfig.agg.type.dslName]: { field: formConfig.field.name }
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return json;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createJobForSaving(job) {
|
|
||||||
const newJob = angular.copy(job);
|
|
||||||
delete newJob.datafeed_config;
|
|
||||||
return newJob;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.getJobFromConfig = function (formConfig) {
|
|
||||||
const job = mlJobService.getBlankJob();
|
const job = mlJobService.getBlankJob();
|
||||||
job.data_description.time_field = formConfig.timeField;
|
job.data_description.time_field = formConfig.timeField;
|
||||||
|
|
||||||
|
@ -250,7 +162,6 @@ module.service('mlSingleMetricJobService', function (
|
||||||
const bucketSpanSeconds = parseInterval(formConfig.bucketSpan).asSeconds();
|
const bucketSpanSeconds = parseInterval(formConfig.bucketSpan).asSeconds();
|
||||||
|
|
||||||
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
const indices = formConfig.indexPattern.title.split(',').map(i => i.trim());
|
||||||
|
|
||||||
job.datafeed_config = {
|
job.datafeed_config = {
|
||||||
query,
|
query,
|
||||||
indices,
|
indices,
|
||||||
|
@ -356,13 +267,11 @@ module.service('mlSingleMetricJobService', function (
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('auto created job: ', job);
|
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.createJob = function (formConfig) {
|
createJob(formConfig) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
this.job = this.getJobFromConfig(formConfig);
|
this.job = this.getJobFromConfig(formConfig);
|
||||||
const job = createJobForSaving(this.job);
|
const job = createJobForSaving(this.job);
|
||||||
|
@ -371,31 +280,31 @@ module.service('mlSingleMetricJobService', function (
|
||||||
mlJobService.saveNewJob(job)
|
mlJobService.saveNewJob(job)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.success) {
|
if (resp.success) {
|
||||||
deferred.resolve(this.job);
|
resolve(this.job);
|
||||||
} else {
|
} else {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.startDatafeed = function (formConfig) {
|
startDatafeed(formConfig) {
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
return mlJobService.startDatafeed(datafeedId, formConfig.jobId, formConfig.start, formConfig.end);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.stopDatafeed = function (formConfig) {
|
stopDatafeed(formConfig) {
|
||||||
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
const datafeedId = mlJobService.getDatafeedId(formConfig.jobId);
|
||||||
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
return mlJobService.stopDatafeed(datafeedId, formConfig.jobId);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.checkDatafeedState = function (formConfig) {
|
checkDatafeedState(formConfig) {
|
||||||
return mlJobService.updateSingleJobDatafeedState(formConfig.jobId);
|
return mlJobService.updateSingleJobDatafeedState(formConfig.jobId);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.loadModelData = function (formConfig) {
|
loadModelData(formConfig) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
let start = formConfig.start;
|
let start = formConfig.start;
|
||||||
|
|
||||||
|
@ -443,17 +352,17 @@ module.service('mlSingleMetricJobService', function (
|
||||||
const pcnt = ((time - formConfig.start + formConfig.resultsIntervalSeconds) / (formConfig.end - formConfig.start) * 100);
|
const pcnt = ((time - formConfig.start + formConfig.resultsIntervalSeconds) / (formConfig.end - formConfig.start) * 100);
|
||||||
this.chartData.percentComplete = Math.round(pcnt);
|
this.chartData.percentComplete = Math.round(pcnt);
|
||||||
|
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
deferred.reject(this.chartData);
|
reject(this.chartData);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.loadSwimlaneData = function (formConfig) {
|
loadSwimlaneData(formConfig) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve) => {
|
||||||
|
|
||||||
mlResultsService.getScoresByBucket(
|
mlResultsService.getScoresByBucket(
|
||||||
[formConfig.jobId],
|
[formConfig.jobId],
|
||||||
|
@ -466,12 +375,100 @@ module.service('mlSingleMetricJobService', function (
|
||||||
const jobResults = data.results[formConfig.jobId];
|
const jobResults = data.results[formConfig.jobId];
|
||||||
this.chartData.swimlane = processSwimlaneResults(jobResults);
|
this.chartData.swimlane = processSwimlaneResults(jobResults);
|
||||||
this.chartData.swimlaneInterval = formConfig.resultsIntervalSeconds * 1000;
|
this.chartData.swimlaneInterval = formConfig.resultsIntervalSeconds * 1000;
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
deferred.resolve(this.chartData);
|
resolve(this.chartData);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new SingleMetricJobService();
|
||||||
|
}
|
||||||
|
|
||||||
|
function processLineChartResults(data, scale = 1) {
|
||||||
|
const lineData = [];
|
||||||
|
_.each(data, (dataForTime, t) => {
|
||||||
|
const time = +t;
|
||||||
|
const date = new Date(time);
|
||||||
|
lineData.push({
|
||||||
|
date: date,
|
||||||
|
time: time,
|
||||||
|
lower: (dataForTime.modelLower * scale),
|
||||||
|
value: dataForTime.actual,
|
||||||
|
upper: (dataForTime.modelUpper * scale)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return _.sortBy(lineData, 'time');
|
||||||
|
}
|
||||||
|
|
||||||
|
function processSwimlaneResults(bucketScoreData, init) {
|
||||||
|
// create a dataset in format used by the model plot chart.
|
||||||
|
// create empty swimlane dataset
|
||||||
|
// i.e. array of Objects with keys date (JavaScript date), value, lower and upper.
|
||||||
|
const swimlaneData = [];
|
||||||
|
_.each(bucketScoreData, (value, t) => {
|
||||||
|
const time = +t;
|
||||||
|
const date = new Date(time);
|
||||||
|
value = init ? 0 : value;
|
||||||
|
swimlaneData.push({
|
||||||
|
date,
|
||||||
|
time,
|
||||||
|
value,
|
||||||
|
color: ''
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return swimlaneData;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getSearchJsonFromConfig(formConfig) {
|
||||||
|
const interval = formConfig.chartInterval.getInterval().asMilliseconds() + 'ms';
|
||||||
|
// clone the query as we're modifying it
|
||||||
|
const query = _.cloneDeep(formConfig.combinedQuery);
|
||||||
|
|
||||||
|
const json = {
|
||||||
|
index: formConfig.indexPattern.title,
|
||||||
|
size: 0,
|
||||||
|
body: {
|
||||||
|
query: {},
|
||||||
|
aggs: {
|
||||||
|
times: {
|
||||||
|
date_histogram: {
|
||||||
|
field: formConfig.timeField,
|
||||||
|
interval: interval,
|
||||||
|
min_doc_count: 0,
|
||||||
|
extended_bounds: {
|
||||||
|
min: formConfig.start,
|
||||||
|
max: formConfig.end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
});
|
|
||||||
|
query.bool.must.push({
|
||||||
|
range: {
|
||||||
|
[formConfig.timeField]: {
|
||||||
|
gte: formConfig.start,
|
||||||
|
lte: formConfig.end,
|
||||||
|
format: formConfig.format
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
json.body.query = query;
|
||||||
|
|
||||||
|
if (formConfig.field !== null) {
|
||||||
|
json.body.aggs.times.aggs = {
|
||||||
|
field_value: {
|
||||||
|
[formConfig.agg.type.dslName]: { field: formConfig.field.name }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
let defaults = {
|
let defaults = {
|
||||||
anomaly_detectors: {},
|
anomaly_detectors: {},
|
||||||
|
@ -12,7 +12,7 @@ let defaults = {
|
||||||
};
|
};
|
||||||
let limits = {};
|
let limits = {};
|
||||||
|
|
||||||
export function loadNewJobDefaults(ml) {
|
export function loadNewJobDefaults() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
ml.mlInfo()
|
ml.mlInfo()
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
|
|
@ -10,6 +10,7 @@ import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
import { migrateFilter } from 'ui/courier/data_source/_migrate_filter.js';
|
import { migrateFilter } from 'ui/courier/data_source/_migrate_filter.js';
|
||||||
import { addItemToRecentlyAccessed } from 'plugins/ml/util/recently_accessed';
|
import { addItemToRecentlyAccessed } from 'plugins/ml/util/recently_accessed';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
export function getQueryFromSavedSearch(formConfig) {
|
export function getQueryFromSavedSearch(formConfig) {
|
||||||
const must = [];
|
const must = [];
|
||||||
|
@ -103,12 +104,19 @@ export function createResultsUrl(jobIds, start, end, resultsPage) {
|
||||||
return path;
|
return path;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createJobForSaving(job) {
|
||||||
|
const newJob = _.cloneDeep(job);
|
||||||
|
delete newJob.datafeed_config;
|
||||||
|
return newJob;
|
||||||
|
}
|
||||||
|
|
||||||
export function addNewJobToRecentlyAccessed(jobId, resultsUrl) {
|
export function addNewJobToRecentlyAccessed(jobId, resultsUrl) {
|
||||||
const urlParts = resultsUrl.match(/ml#\/(.+?)(\?.+)/);
|
const urlParts = resultsUrl.match(/ml#\/(.+?)(\?.+)/);
|
||||||
addItemToRecentlyAccessed(urlParts[1], jobId, urlParts[2]);
|
addItemToRecentlyAccessed(urlParts[1], jobId, urlParts[2]);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function moveToAdvancedJobCreationProvider(mlJobService, $location) {
|
export function moveToAdvancedJobCreationProvider(Private, $location) {
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
return function moveToAdvancedJobCreation(job) {
|
return function moveToAdvancedJobCreation(job) {
|
||||||
mlJobService.currentJob = job;
|
mlJobService.currentJob = job;
|
||||||
$location.path('jobs/new_job/advanced');
|
$location.path('jobs/new_job/advanced');
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
* you may not use this file except in compliance with the Elastic License.
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
let mlNodeCount = 0;
|
let mlNodeCount = 0;
|
||||||
let userHasPermissionToViewMlNodeCount = false;
|
let userHasPermissionToViewMlNodeCount = false;
|
||||||
|
|
||||||
export function checkMlNodesAvailable(ml, kbnUrl) {
|
export function checkMlNodesAvailable(kbnUrl) {
|
||||||
getMlNodeCount(ml).then((nodes) => {
|
getMlNodeCount().then((nodes) => {
|
||||||
if (nodes.count !== undefined && nodes.count > 0) {
|
if (nodes.count !== undefined && nodes.count > 0) {
|
||||||
Promise.resolve();
|
Promise.resolve();
|
||||||
} else {
|
} else {
|
||||||
|
@ -20,7 +20,7 @@ export function checkMlNodesAvailable(ml, kbnUrl) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getMlNodeCount(ml) {
|
export function getMlNodeCount() {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
ml.mlNodeCount()
|
ml.mlNodeCount()
|
||||||
.then((nodes) => {
|
.then((nodes) => {
|
||||||
|
|
|
@ -5,8 +5,9 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
export function privilegesProvider(Promise, ml) {
|
export function privilegesProvider() {
|
||||||
|
|
||||||
function getPrivileges() {
|
function getPrivileges() {
|
||||||
const privileges = {
|
const privileges = {
|
||||||
|
|
|
@ -8,18 +8,25 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
const module = uiModules.get('apps/ml');
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
|
||||||
module.service('mlCalendarService', function ($q, ml, mlJobService, mlMessageBarService) {
|
let calendarService = undefined;
|
||||||
|
|
||||||
|
export function CalendarServiceProvider($q, Private, mlMessageBarService) {
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
|
class CalendarService {
|
||||||
|
constructor() {
|
||||||
this.calendars = [];
|
this.calendars = [];
|
||||||
// list of calendar ids per job id
|
// list of calendar ids per job id
|
||||||
this.jobCalendars = {};
|
this.jobCalendars = {};
|
||||||
// list of calendar ids per group id
|
// list of calendar ids per group id
|
||||||
this.groupCalendars = {};
|
this.groupCalendars = {};
|
||||||
|
}
|
||||||
|
|
||||||
this.loadCalendars = function (jobs) {
|
loadCalendars(jobs) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
let calendars = [];
|
let calendars = [];
|
||||||
jobs.forEach((j) => {
|
jobs.forEach((j) => {
|
||||||
|
@ -77,10 +84,16 @@ module.service('mlCalendarService', function ($q, ml, mlJobService, mlMessageBar
|
||||||
reject({ calendars, err });
|
reject({ calendars, err });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// get the list of calendar groups
|
// get the list of calendar groups
|
||||||
this.getCalendarGroups = function () {
|
getCalendarGroups() {
|
||||||
return Object.keys(this.groupCalendars).map(gId => ({ id: gId }));
|
return Object.keys(this.groupCalendars).map(id => ({ id }));
|
||||||
};
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
|
if (calendarService === undefined) {
|
||||||
|
calendarService = new CalendarService();
|
||||||
|
}
|
||||||
|
return calendarService;
|
||||||
|
}
|
||||||
|
|
|
@ -11,32 +11,31 @@ import _ from 'lodash';
|
||||||
import 'ui/courier';
|
import 'ui/courier';
|
||||||
import { mlFunctionToESAggregation } from 'plugins/ml/../common/util/job_utils';
|
import { mlFunctionToESAggregation } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { getIndexPatternProvider } from 'plugins/ml/util/index_utils';
|
import { getIndexPatternProvider } from 'plugins/ml/util/index_utils';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
// Service for accessing FieldFormat objects configured for a Kibana index pattern
|
// Service for accessing FieldFormat objects configured for a Kibana index pattern
|
||||||
// for use in formatting the actual and typical values from anomalies.
|
// for use in formatting the actual and typical values from anomalies.
|
||||||
module.service('mlFieldFormatService', function (
|
export function FieldFormatServiceProvider(
|
||||||
$q,
|
$q,
|
||||||
courier,
|
courier,
|
||||||
Private,
|
Private) {
|
||||||
mlJobService) {
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
|
||||||
const indexPatternIdsByJob = {};
|
|
||||||
const formatsByJob = {};
|
|
||||||
|
|
||||||
const getIndexPattern = Private(getIndexPatternProvider);
|
const getIndexPattern = Private(getIndexPatternProvider);
|
||||||
|
|
||||||
|
class FieldFormatService {
|
||||||
|
constructor() {
|
||||||
|
this.indexPatternIdsByJob = {};
|
||||||
|
this.formatsByJob = {};
|
||||||
|
}
|
||||||
|
|
||||||
// Populate the service with the FieldFormats for the list of jobs with the
|
// Populate the service with the FieldFormats for the list of jobs with the
|
||||||
// specified IDs. List of Kibana index patterns is passed, with a title
|
// specified IDs. List of Kibana index patterns is passed, with a title
|
||||||
// attribute set in each pattern which will be compared to the index pattern
|
// attribute set in each pattern which will be compared to the index pattern
|
||||||
// configured in the datafeed of each job.
|
// configured in the datafeed of each job.
|
||||||
// Builds a map of Kibana FieldFormats (ui/field_formats/field_format.js)
|
// Builds a map of Kibana FieldFormats (ui/field_formats/field_format.js)
|
||||||
// against detector index by job ID.
|
// against detector index by job ID.
|
||||||
this.populateFormats = function (jobIds, indexPatterns) {
|
populateFormats(jobIds, indexPatterns) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
// Populate a map of index pattern IDs against job ID, by finding the ID of the index
|
// Populate a map of index pattern IDs against job ID, by finding the ID of the index
|
||||||
// pattern with a title attribute which matches the index configured in the datafeed.
|
// pattern with a title attribute which matches the index configured in the datafeed.
|
||||||
// If a Kibana index pattern has not been created
|
// If a Kibana index pattern has not been created
|
||||||
|
@ -52,39 +51,38 @@ module.service('mlFieldFormatService', function (
|
||||||
|
|
||||||
// Check if index pattern has been configured to match the index in datafeed.
|
// Check if index pattern has been configured to match the index in datafeed.
|
||||||
if (indexPattern !== undefined) {
|
if (indexPattern !== undefined) {
|
||||||
indexPatternIdsByJob[jobId] = indexPattern.id;
|
this.indexPatternIdsByJob[jobId] = indexPattern.id;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const promises = jobIds.map(jobId => $q.all([
|
const promises = jobIds.map(jobId => $q.all([
|
||||||
getFormatsForJob(jobId)
|
this.getFormatsForJob(jobId)
|
||||||
]));
|
]));
|
||||||
|
|
||||||
$q.all(promises).then((fmtsByJobByDetector) => {
|
$q.all(promises).then((fmtsByJobByDetector) => {
|
||||||
_.each(fmtsByJobByDetector, (formatsByDetector, index) => {
|
_.each(fmtsByJobByDetector, (formatsByDetector, index) => {
|
||||||
formatsByJob[jobIds[index]] = formatsByDetector[0];
|
this.formatsByJob[jobIds[index]] = formatsByDetector[0];
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(formatsByJob);
|
resolve(this.formatsByJob);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
console.log('mlFieldFormatService error populating formats:', err);
|
console.log('fieldFormatService error populating formats:', err);
|
||||||
deferred.reject({ formats: {}, err });
|
reject({ formats: {}, err });
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Return the FieldFormat to use for formatting values from
|
// Return the FieldFormat to use for formatting values from
|
||||||
// the detector from the job with the specified ID.
|
// the detector from the job with the specified ID.
|
||||||
this.getFieldFormat = function (jobId, detectorIndex) {
|
getFieldFormat(jobId, detectorIndex) {
|
||||||
return _.get(formatsByJob, [jobId, detectorIndex]);
|
return _.get(this.formatsByJob, [jobId, detectorIndex]);
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Utility for returning the FieldFormat from a full populated Kibana index pattern object
|
// Utility for returning the FieldFormat from a full populated Kibana index pattern object
|
||||||
// containing the list of fields by name with their formats.
|
// containing the list of fields by name with their formats.
|
||||||
this.getFieldFormatFromIndexPattern = function (fullIndexPattern, fieldName, esAggName) {
|
getFieldFormatFromIndexPattern(fullIndexPattern, fieldName, esAggName) {
|
||||||
// Don't use the field formatter for distinct count detectors as
|
// Don't use the field formatter for distinct count detectors as
|
||||||
// e.g. distinct_count(clientip) should be formatted as a count, not as an IP address.
|
// e.g. distinct_count(clientip) should be formatted as a count, not as an IP address.
|
||||||
let fieldFormat = undefined;
|
let fieldFormat = undefined;
|
||||||
|
@ -94,16 +92,16 @@ module.service('mlFieldFormatService', function (
|
||||||
}
|
}
|
||||||
|
|
||||||
return fieldFormat;
|
return fieldFormat;
|
||||||
};
|
}
|
||||||
|
|
||||||
function getFormatsForJob(jobId) {
|
getFormatsForJob(jobId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
const jobObj = mlJobService.getJob(jobId);
|
const jobObj = mlJobService.getJob(jobId);
|
||||||
const detectors = jobObj.analysis_config.detectors || [];
|
const detectors = jobObj.analysis_config.detectors || [];
|
||||||
const formatsByDetector = {};
|
const formatsByDetector = {};
|
||||||
|
|
||||||
const indexPatternId = indexPatternIdsByJob[jobId];
|
const indexPatternId = this.indexPatternIdsByJob[jobId];
|
||||||
if (indexPatternId !== undefined) {
|
if (indexPatternId !== undefined) {
|
||||||
// Load the full index pattern configuration to obtain the formats of each field.
|
// Load the full index pattern configuration to obtain the formats of each field.
|
||||||
getIndexPattern(indexPatternId)
|
getIndexPattern(indexPatternId)
|
||||||
|
@ -119,16 +117,16 @@ module.service('mlFieldFormatService', function (
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(formatsByDetector);
|
resolve(formatsByDetector);
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
deferred.reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(formatsByDetector);
|
resolve(formatsByDetector);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
return new FieldFormatService();
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Service for carrying out queries to obtain data
|
|
||||||
// specific to fields in Elasticsearch indices.
|
|
||||||
|
|
||||||
export function FieldsServiceProvider(es, ml) {
|
|
||||||
|
|
||||||
// Obtains the cardinality of one or more fields.
|
|
||||||
// Returns an Object whose keys are the names of the fields,
|
|
||||||
// with values equal to the cardinality of the field.
|
|
||||||
function getCardinalityOfFields(
|
|
||||||
index,
|
|
||||||
types,
|
|
||||||
fieldNames,
|
|
||||||
query,
|
|
||||||
timeFieldName,
|
|
||||||
earliestMs,
|
|
||||||
latestMs) {
|
|
||||||
|
|
||||||
return ml.getCardinalityOfFields({
|
|
||||||
index,
|
|
||||||
types,
|
|
||||||
fieldNames,
|
|
||||||
query,
|
|
||||||
timeFieldName,
|
|
||||||
earliestMs,
|
|
||||||
latestMs
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the range of the specified time field.
|
|
||||||
// Returns an Object containing start and end properties,
|
|
||||||
// holding the value as an epoch (ms since the Unix epoch)
|
|
||||||
// and as a formatted string.
|
|
||||||
function getTimeFieldRange(
|
|
||||||
index,
|
|
||||||
timeFieldName,
|
|
||||||
query) {
|
|
||||||
|
|
||||||
return ml.getTimeFieldRange({
|
|
||||||
index,
|
|
||||||
timeFieldName,
|
|
||||||
query
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
getCardinalityOfFields,
|
|
||||||
getTimeFieldRange
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -11,24 +11,22 @@
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
export function ForecastServiceProvider(es, $q) {
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlForecastService', function ($q, es, ml) {
|
|
||||||
|
|
||||||
// Gets a basic summary of the most recently run forecasts for the specified
|
// Gets a basic summary of the most recently run forecasts for the specified
|
||||||
// job, with results at or later than the supplied timestamp.
|
// job, with results at or later than the supplied timestamp.
|
||||||
// Extra query object can be supplied, or pass null if no additional query.
|
// Extra query object can be supplied, or pass null if no additional query.
|
||||||
// Returned response contains a forecasts property, which is an array of objects
|
// Returned response contains a forecasts property, which is an array of objects
|
||||||
// containing id, earliest and latest keys.
|
// containing id, earliest and latest keys.
|
||||||
this.getForecastsSummary = function (
|
function getForecastsSummary(
|
||||||
job,
|
job,
|
||||||
query,
|
query,
|
||||||
earliestMs,
|
earliestMs,
|
||||||
maxResults
|
maxResults
|
||||||
) {
|
) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
forecasts: []
|
forecasts: []
|
||||||
|
@ -74,27 +72,24 @@ module.service('mlForecastService', function ($q, es, ml) {
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
if (resp.hits.total !== 0) {
|
if (resp.hits.total !== 0) {
|
||||||
_.each(resp.hits.hits, (hit) => {
|
obj.forecasts = resp.hits.hits.map(hit => hit._source);
|
||||||
obj.forecasts.push(hit._source);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
return deferred.promise;
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// Obtains the earliest and latest timestamps for the forecast data from
|
// Obtains the earliest and latest timestamps for the forecast data from
|
||||||
// the forecast with the specified ID.
|
// the forecast with the specified ID.
|
||||||
// Returned response contains earliest and latest properties which are the
|
// Returned response contains earliest and latest properties which are the
|
||||||
// timestamps of the first and last model_forecast results.
|
// timestamps of the first and last model_forecast results.
|
||||||
this.getForecastDateRange = function (job, forecastId) {
|
function getForecastDateRange(job, forecastId) {
|
||||||
|
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
earliest: null,
|
earliest: null,
|
||||||
|
@ -146,20 +141,20 @@ module.service('mlForecastService', function ($q, es, ml) {
|
||||||
obj.earliest = _.get(resp, 'aggregations.earliest.value', null);
|
obj.earliest = _.get(resp, 'aggregations.earliest.value', null);
|
||||||
obj.latest = _.get(resp, 'aggregations.latest.value', null);
|
obj.latest = _.get(resp, 'aggregations.latest.value', null);
|
||||||
if (obj.earliest === null || obj.latest === null) {
|
if (obj.earliest === null || obj.latest === null) {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains the requested forecast model data for the forecast with the specified ID.
|
// Obtains the requested forecast model data for the forecast with the specified ID.
|
||||||
this.getForecastData = function (
|
function getForecastData(
|
||||||
job,
|
job,
|
||||||
detectorIndex,
|
detectorIndex,
|
||||||
forecastId,
|
forecastId,
|
||||||
|
@ -198,7 +193,7 @@ module.service('mlForecastService', function ($q, es, ml) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
results: {}
|
results: {}
|
||||||
|
@ -302,38 +297,38 @@ module.service('mlForecastService', function ($q, es, ml) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Runs a forecast
|
// Runs a forecast
|
||||||
this.runForecast = function (jobId, duration) {
|
function runForecast(jobId, duration) {
|
||||||
console.log('ML forecast service run forecast with duration:', duration);
|
console.log('ML forecast service run forecast with duration:', duration);
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
ml.forecast({
|
ml.forecast({
|
||||||
jobId,
|
jobId,
|
||||||
duration
|
duration
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
deferred.resolve(resp);
|
resolve(resp);
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
deferred.reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Gets stats for a forecast that has been run on the specified job.
|
// Gets stats for a forecast that has been run on the specified job.
|
||||||
// Returned response contains a stats property, including
|
// Returned response contains a stats property, including
|
||||||
// forecast_progress (a value from 0 to 1),
|
// forecast_progress (a value from 0 to 1),
|
||||||
// and forecast_status ('finished' when complete) properties.
|
// and forecast_status ('finished' when complete) properties.
|
||||||
this.getForecastRequestStats = function (job, forecastId) {
|
function getForecastRequestStats(job, forecastId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
stats: {}
|
stats: {}
|
||||||
|
@ -369,13 +364,21 @@ module.service('mlForecastService', function ($q, es, ml) {
|
||||||
if (resp.hits.total !== 0) {
|
if (resp.hits.total !== 0) {
|
||||||
obj.stats = _.first(resp.hits.hits)._source;
|
obj.stats = _.first(resp.hits.hits)._source;
|
||||||
}
|
}
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getForecastsSummary,
|
||||||
|
getForecastDateRange,
|
||||||
|
getForecastData,
|
||||||
|
runForecast,
|
||||||
|
getForecastRequestStats
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
}
|
||||||
|
|
|
@ -8,43 +8,39 @@
|
||||||
|
|
||||||
// service for interacting with the server
|
// service for interacting with the server
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import chrome from 'ui/chrome';
|
||||||
const module = uiModules.get('apps/ml');
|
import 'isomorphic-fetch';
|
||||||
|
|
||||||
import { addSystemApiHeader } from 'ui/system_api';
|
import { addSystemApiHeader } from 'ui/system_api';
|
||||||
|
|
||||||
module.service('prlHttpService', function ($http, $q) {
|
export function http(options) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
// request function returns a promise
|
|
||||||
// once resolved, just the data response is returned
|
|
||||||
this.request = function (options) {
|
|
||||||
if(options && options.url) {
|
if(options && options.url) {
|
||||||
let url = '';
|
let url = '';
|
||||||
url = url + (options.url || '');
|
url = url + (options.url || '');
|
||||||
const headers = addSystemApiHeader({});
|
const headers = addSystemApiHeader({
|
||||||
const allHeaders = (options.headers === undefined) ?
|
'Content-Type': 'application/json',
|
||||||
headers :
|
'kbn-version': chrome.getXsrfToken(),
|
||||||
Object.assign(options.headers, headers);
|
...options.headers
|
||||||
|
|
||||||
|
|
||||||
const deferred = $q.defer();
|
|
||||||
|
|
||||||
$http({
|
|
||||||
url: url,
|
|
||||||
method: (options.method || 'GET'),
|
|
||||||
headers: (allHeaders),
|
|
||||||
params: (options.params || {}),
|
|
||||||
data: (options.data || null)
|
|
||||||
})
|
|
||||||
.then(function successCallback(response) {
|
|
||||||
deferred.resolve(response.data);
|
|
||||||
}, function errorCallback(response) {
|
|
||||||
deferred.reject(response.data);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
const allHeaders = (options.headers === undefined) ? headers : { ...options.headers, ...headers };
|
||||||
|
const body = (options.data === undefined) ? null : JSON.stringify(options.data);
|
||||||
|
|
||||||
|
fetch(url, {
|
||||||
|
method: (options.method || 'GET'),
|
||||||
|
headers: (allHeaders),
|
||||||
|
credentials: 'same-origin',
|
||||||
|
body,
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
resolve(resp.json());
|
||||||
|
})
|
||||||
|
.catch((resp) => {
|
||||||
|
reject(resp);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
reject();
|
||||||
}
|
}
|
||||||
};
|
});
|
||||||
|
}
|
||||||
});
|
|
||||||
|
|
||||||
|
|
170
x-pack/plugins/ml/public/services/job_messages_service.js
Normal file
170
x-pack/plugins/ml/public/services/job_messages_service.js
Normal file
|
@ -0,0 +1,170 @@
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Service for carrying out Elasticsearch queries to obtain data for the
|
||||||
|
// Ml Results dashboards.
|
||||||
|
import { ML_NOTIFICATION_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
||||||
|
|
||||||
|
export function JobMessagesServiceProvider(es, $q) {
|
||||||
|
// search for audit messages, jobId is optional.
|
||||||
|
// without it, all jobs will be listed.
|
||||||
|
// fromRange should be a string formatted in ES time units. e.g. 12h, 1d, 7d
|
||||||
|
function getJobAuditMessages(fromRange, jobId) {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
|
let jobFilter = {};
|
||||||
|
// if no jobId specified, load all of the messages
|
||||||
|
if (jobId !== undefined) {
|
||||||
|
jobFilter = {
|
||||||
|
bool: {
|
||||||
|
should: [
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
job_id: '' // catch system messages
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
term: {
|
||||||
|
job_id: jobId // messages for specified jobId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
let timeFilter = {};
|
||||||
|
if (fromRange !== undefined && fromRange !== '') {
|
||||||
|
timeFilter = {
|
||||||
|
range: {
|
||||||
|
timestamp: {
|
||||||
|
gte: `now-${fromRange}`,
|
||||||
|
lte: 'now'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
es.search({
|
||||||
|
index: ML_NOTIFICATION_INDEX_PATTERN,
|
||||||
|
ignore_unavailable: true,
|
||||||
|
size: 1000,
|
||||||
|
body:
|
||||||
|
{
|
||||||
|
sort: [
|
||||||
|
{ timestamp: { order: 'asc' } },
|
||||||
|
{ job_id: { order: 'asc' } }
|
||||||
|
],
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
filter: [
|
||||||
|
{
|
||||||
|
bool: {
|
||||||
|
must_not: {
|
||||||
|
term: {
|
||||||
|
level: 'activity'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
jobFilter,
|
||||||
|
timeFilter
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
let messages = [];
|
||||||
|
if (resp.hits.total !== 0) {
|
||||||
|
messages = resp.hits.hits.map(hit => hit._source);
|
||||||
|
}
|
||||||
|
resolve({ messages });
|
||||||
|
})
|
||||||
|
.catch((resp) => {
|
||||||
|
reject(resp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// search highest, most recent audit messages for all jobs for the last 24hrs.
|
||||||
|
function getAuditMessagesSummary() {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
|
es.search({
|
||||||
|
index: ML_NOTIFICATION_INDEX_PATTERN,
|
||||||
|
ignore_unavailable: true,
|
||||||
|
size: 0,
|
||||||
|
body: {
|
||||||
|
query: {
|
||||||
|
bool: {
|
||||||
|
filter: {
|
||||||
|
range: {
|
||||||
|
timestamp: {
|
||||||
|
gte: 'now-1d'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
levelsPerJob: {
|
||||||
|
terms: {
|
||||||
|
field: 'job_id',
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
levels: {
|
||||||
|
terms: {
|
||||||
|
field: 'level',
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
latestMessage: {
|
||||||
|
terms: {
|
||||||
|
field: 'message.raw',
|
||||||
|
size: 1,
|
||||||
|
order: {
|
||||||
|
latestMessage: 'desc'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
aggs: {
|
||||||
|
latestMessage: {
|
||||||
|
max: {
|
||||||
|
field: 'timestamp'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then((resp) => {
|
||||||
|
let messagesPerJob = [];
|
||||||
|
if (resp.hits.total !== 0 &&
|
||||||
|
resp.aggregations &&
|
||||||
|
resp.aggregations.levelsPerJob &&
|
||||||
|
resp.aggregations.levelsPerJob.buckets &&
|
||||||
|
resp.aggregations.levelsPerJob.buckets.length) {
|
||||||
|
messagesPerJob = resp.aggregations.levelsPerJob.buckets;
|
||||||
|
}
|
||||||
|
resolve({ messagesPerJob });
|
||||||
|
})
|
||||||
|
.catch((resp) => {
|
||||||
|
reject(resp);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getJobAuditMessages,
|
||||||
|
getAuditMessagesSummary
|
||||||
|
};
|
||||||
|
}
|
|
@ -11,19 +11,21 @@ import angular from 'angular';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import { parseInterval } from 'ui/utils/parse_interval';
|
import { parseInterval } from 'ui/utils/parse_interval';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import { FieldsServiceProvider } from 'plugins/ml/services/fields_service';
|
|
||||||
import { labelDuplicateDetectorDescriptions } from 'plugins/ml/util/anomaly_utils';
|
import { labelDuplicateDetectorDescriptions } from 'plugins/ml/util/anomaly_utils';
|
||||||
import { isWebUrl } from 'plugins/ml/util/string_utils';
|
import { isWebUrl } from 'plugins/ml/util/string_utils';
|
||||||
import { ML_DATA_PREVIEW_COUNT } from 'plugins/ml/../common/util/job_utils';
|
import { ML_DATA_PREVIEW_COUNT } from 'plugins/ml/../common/util/job_utils';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
let jobService = undefined;
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessageBarService, Private) {
|
export function JobServiceProvider($q, es, Private, mlMessageBarService) {
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
let jobs = [];
|
let jobs = [];
|
||||||
let datafeedIds = {};
|
let datafeedIds = {};
|
||||||
|
|
||||||
|
class JobService {
|
||||||
|
constructor() {
|
||||||
this.currentJob = undefined;
|
this.currentJob = undefined;
|
||||||
this.jobs = [];
|
this.jobs = [];
|
||||||
|
|
||||||
|
@ -42,28 +44,9 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
activeDatafeeds: { label: 'Active datafeeds', value: 0, show: true }
|
activeDatafeeds: { label: 'Active datafeeds', value: 0, show: true }
|
||||||
};
|
};
|
||||||
this.jobUrls = {};
|
this.jobUrls = {};
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.getBlankJob = function () {
|
getBlankJob() {
|
||||||
return {
|
return {
|
||||||
job_id: '',
|
job_id: '',
|
||||||
description: '',
|
description: '',
|
||||||
|
@ -81,10 +64,10 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
format: 'delimited'
|
format: 'delimited'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
}
|
||||||
|
|
||||||
this.loadJobs = function () {
|
loadJobs() {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
jobs = [];
|
jobs = [];
|
||||||
datafeedIds = {};
|
datafeedIds = {};
|
||||||
|
|
||||||
|
@ -135,7 +118,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
this.jobs = jobs;
|
this.jobs = jobs;
|
||||||
createJobStats(this.jobs, this.jobStats);
|
createJobStats(this.jobs, this.jobStats);
|
||||||
createJobUrls(this.jobs, this.jobUrls);
|
createJobUrls(this.jobs, this.jobUrls);
|
||||||
deferred.resolve({ jobs: this.jobs });
|
resolve({ jobs: this.jobs });
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -146,16 +129,16 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
});
|
});
|
||||||
|
|
||||||
function error(err) {
|
function error(err) {
|
||||||
console.log('MlJobsList error getting list of jobs:', err);
|
console.log('jobService error getting list of jobs:', err);
|
||||||
msgs.error('Jobs list could not be retrieved');
|
msgs.error('Jobs list could not be retrieved');
|
||||||
msgs.error('', err);
|
msgs.error('', err);
|
||||||
deferred.reject({ jobs, err });
|
reject({ jobs, err });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.refreshJob = function (jobId) {
|
refreshJob(jobId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
ml.jobs({ jobId })
|
ml.jobs({ jobId })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
console.log('refreshJob query response:', resp);
|
console.log('refreshJob query response:', resp);
|
||||||
|
@ -208,7 +191,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
this.jobs = jobs;
|
this.jobs = jobs;
|
||||||
createJobStats(this.jobs, this.jobStats);
|
createJobStats(this.jobs, this.jobStats);
|
||||||
createJobUrls(this.jobs, this.jobUrls);
|
createJobUrls(this.jobs, this.jobUrls);
|
||||||
deferred.resolve({ jobs: this.jobs });
|
resolve({ jobs: this.jobs });
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -220,16 +203,16 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
});
|
});
|
||||||
|
|
||||||
function error(err) {
|
function error(err) {
|
||||||
console.log('MlJobsList error getting list of jobs:', err);
|
console.log('JobService error getting list of jobs:', err);
|
||||||
msgs.error('Jobs list could not be retrieved');
|
msgs.error('Jobs list could not be retrieved');
|
||||||
msgs.error('', err);
|
msgs.error('', err);
|
||||||
deferred.reject({ jobs, err });
|
reject({ jobs, err });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loadDatafeeds = function (datafeedId) {
|
loadDatafeeds(datafeedId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const datafeeds = [];
|
const datafeeds = [];
|
||||||
const sId = (datafeedId !== undefined) ? { datafeed_id: datafeedId } : undefined;
|
const sId = (datafeedId !== undefined) ? { datafeed_id: datafeedId } : undefined;
|
||||||
|
|
||||||
|
@ -252,7 +235,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deferred.resolve({ datafeeds });
|
resolve({ datafeeds });
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
error(err);
|
error(err);
|
||||||
|
@ -265,16 +248,14 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
console.log('loadDatafeeds error getting list of datafeeds:', err);
|
console.log('loadDatafeeds error getting list of datafeeds:', err);
|
||||||
msgs.error('datafeeds list could not be retrieved');
|
msgs.error('datafeeds list could not be retrieved');
|
||||||
msgs.error('', err);
|
msgs.error('', err);
|
||||||
deferred.reject({ jobs, err });
|
reject({ jobs, err });
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
updateSingleJobCounts(jobId) {
|
||||||
|
return $q((resolve, reject) => {
|
||||||
this.updateSingleJobCounts = function (jobId) {
|
console.log('jobService: update job counts and state for ' + jobId);
|
||||||
const deferred = $q.defer();
|
|
||||||
console.log('mlJobService: update job counts and state for ' + jobId);
|
|
||||||
ml.jobStats({ jobId })
|
ml.jobStats({ jobId })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
console.log('updateSingleJobCounts controller query response:', resp);
|
console.log('updateSingleJobCounts controller query response:', resp);
|
||||||
|
@ -315,13 +296,13 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}
|
}
|
||||||
createJobStats(this.jobs, this.jobStats);
|
createJobStats(this.jobs, this.jobStats);
|
||||||
createJobUrls(this.jobs, this.jobUrls);
|
createJobUrls(this.jobs, this.jobUrls);
|
||||||
deferred.resolve({ jobs: this.jobs });
|
resolve({ jobs: this.jobs });
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
error(err);
|
error(err);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deferred.resolve({ jobs: this.jobs });
|
resolve({ jobs: this.jobs });
|
||||||
}
|
}
|
||||||
|
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
|
@ -332,15 +313,15 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
console.log('updateSingleJobCounts error getting job details:', err);
|
console.log('updateSingleJobCounts error getting job details:', err);
|
||||||
msgs.error('Job details could not be retrieved for ' + jobId);
|
msgs.error('Job details could not be retrieved for ' + jobId);
|
||||||
msgs.error('', err);
|
msgs.error('', err);
|
||||||
deferred.reject({ jobs, err });
|
reject({ jobs, err });
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.updateAllJobStats = function () {
|
updateAllJobStats() {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
console.log('mlJobService: update all jobs counts and state');
|
console.log('jobService: update all jobs counts and state');
|
||||||
ml.jobStats().then((resp) => {
|
ml.jobStats().then((resp) => {
|
||||||
console.log('updateAllJobStats controller query response:', resp);
|
console.log('updateAllJobStats controller query response:', resp);
|
||||||
let newJobsAdded = false;
|
let newJobsAdded = false;
|
||||||
|
@ -403,7 +384,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
if (newJobsAdded || resp.jobs.length !== jobs.length) {
|
if (newJobsAdded || resp.jobs.length !== jobs.length) {
|
||||||
console.log('updateAllJobStats: number of jobs differs. reloading all jobs');
|
console.log('updateAllJobStats: number of jobs differs. reloading all jobs');
|
||||||
this.loadJobs().then(() => {
|
this.loadJobs().then(() => {
|
||||||
deferred.resolve({ jobs: this.jobs, listChanged: true });
|
resolve({ jobs: this.jobs, listChanged: true });
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
error(err);
|
error(err);
|
||||||
|
@ -411,7 +392,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
} else {
|
} else {
|
||||||
createJobStats(this.jobs, this.jobStats);
|
createJobStats(this.jobs, this.jobStats);
|
||||||
createJobUrls(this.jobs, this.jobUrls);
|
createJobUrls(this.jobs, this.jobUrls);
|
||||||
deferred.resolve({ jobs: this.jobs, listChanged: false });
|
resolve({ jobs: this.jobs, listChanged: false });
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch((err) => {
|
.catch((err) => {
|
||||||
|
@ -426,13 +407,13 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
console.log('updateAllJobStats error getting list job details:', err);
|
console.log('updateAllJobStats error getting list job details:', err);
|
||||||
msgs.error('Job details could not be retrieved');
|
msgs.error('Job details could not be retrieved');
|
||||||
msgs.error('', err);
|
msgs.error('', err);
|
||||||
deferred.reject({ jobs, err });
|
reject({ jobs, err });
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getRunningJobs = function () {
|
getRunningJobs() {
|
||||||
const runningJobs = [];
|
const runningJobs = [];
|
||||||
_.each(jobs, (job) => {
|
_.each(jobs, (job) => {
|
||||||
if (job.datafeed_config && job.datafeed_config.state === 'started') {
|
if (job.datafeed_config && job.datafeed_config.state === 'started') {
|
||||||
|
@ -440,10 +421,10 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return runningJobs;
|
return runningJobs;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.updateSingleJobDatafeedState = function (jobId) {
|
updateSingleJobDatafeedState(jobId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
const datafeedId = this.getDatafeedId(jobId);
|
const datafeedId = this.getDatafeedId(jobId);
|
||||||
|
|
||||||
|
@ -455,30 +436,30 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
if (datafeeds && datafeeds.length) {
|
if (datafeeds && datafeeds.length) {
|
||||||
state = datafeeds[0].state;
|
state = datafeeds[0].state;
|
||||||
}
|
}
|
||||||
deferred.resolve(state);
|
resolve(state);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.saveNewJob = function (job) {
|
saveNewJob(job) {
|
||||||
// run then and catch through the same check
|
// run then and catch through the same check
|
||||||
const func = function (resp) {
|
function func(resp) {
|
||||||
console.log('Response for job query:', resp);
|
console.log('Response for job query:', resp);
|
||||||
const success = checkSaveResponse(resp, job);
|
const success = checkSaveResponse(resp, job);
|
||||||
return { success, job, resp };
|
return { success, job, resp };
|
||||||
};
|
}
|
||||||
|
|
||||||
// return the promise chain
|
// return the promise chain
|
||||||
return ml.addJob({ jobId: job.job_id, job })
|
return $q.when(ml.addJob({ jobId: job.job_id, job }))
|
||||||
.then(func).catch(func);
|
.then(func).catch(func);
|
||||||
};
|
}
|
||||||
|
|
||||||
this.deleteJob = function (job, statusIn) {
|
deleteJob(job, statusIn) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const status = statusIn || { deleteDatafeed: 0, deleteJob: 0, errorMessage: '' };
|
const status = statusIn || { deleteDatafeed: 0, deleteJob: 0, errorMessage: '' };
|
||||||
|
|
||||||
// chain of endpoint calls to delete a job.
|
// chain of endpoint calls to delete a job.
|
||||||
|
@ -504,7 +485,7 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
ml.forceDeleteJob({ jobId: job.job_id })
|
ml.forceDeleteJob({ jobId: job.job_id })
|
||||||
.then(() => {
|
.then(() => {
|
||||||
status.deleteJob = 1;
|
status.deleteJob = 1;
|
||||||
deferred.resolve({ success: true });
|
resolve({ success: true });
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
status.deleteJob = -1;
|
status.deleteJob = -1;
|
||||||
|
@ -516,13 +497,13 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
if (resp.statusCode === 500) {
|
if (resp.statusCode === 500) {
|
||||||
status.errorMessage = txt;
|
status.errorMessage = txt;
|
||||||
}
|
}
|
||||||
deferred.reject({ success: false });
|
reject({ success: false });
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.cloneJob = function (job) {
|
cloneJob(job) {
|
||||||
// create a deep copy of a job object
|
// create a deep copy of a job object
|
||||||
// also remove items from the job which are set by the server and not needed
|
// also remove items from the job which are set by the server and not needed
|
||||||
// in the future this formatting could be optional
|
// in the future this formatting could be optional
|
||||||
|
@ -571,11 +552,11 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
return tempJob;
|
return tempJob;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.updateJob = function (jobId, job) {
|
updateJob(jobId, job) {
|
||||||
// return the promise chain
|
// return the promise chain
|
||||||
return ml.updateJob({ jobId, job })
|
return $q.when(ml.updateJob({ jobId, job }))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
console.log('update job', resp);
|
console.log('update job', resp);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
|
@ -584,11 +565,11 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
console.log('update job', err);
|
console.log('update job', err);
|
||||||
return { success: false, message: err.message };
|
return { success: false, message: err.message };
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.validateJob = function (obj) {
|
validateJob(obj) {
|
||||||
// return the promise chain
|
// return the promise chain
|
||||||
return ml.validateJob(obj)
|
return $q.when(ml.validateJob(obj))
|
||||||
.then((messages) => {
|
.then((messages) => {
|
||||||
console.log('validate job', messages);
|
console.log('validate job', messages);
|
||||||
return { success: true, messages };
|
return { success: true, messages };
|
||||||
|
@ -603,24 +584,24 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// find a job based on the id
|
// find a job based on the id
|
||||||
this.getJob = function (jobId) {
|
getJob(jobId) {
|
||||||
const job = _.find(jobs, (j) => {
|
const job = _.find(jobs, (j) => {
|
||||||
return j.job_id === jobId;
|
return j.job_id === jobId;
|
||||||
});
|
});
|
||||||
|
|
||||||
return job;
|
return job;
|
||||||
};
|
}
|
||||||
|
|
||||||
// use elasticsearch to load basic information on jobs, as used by various result
|
// use elasticsearch to load basic information on jobs, as used by various result
|
||||||
// dashboards in the Ml plugin. Returned response contains a jobs property,
|
// dashboards in the Ml plugin. Returned response contains a jobs property,
|
||||||
// which is an array of objects containing id, description, bucketSpanSeconds, detectors
|
// which is an array of objects containing id, description, bucketSpanSeconds, detectors
|
||||||
// and detectorDescriptions properties, plus a customUrls key if custom URLs
|
// and detectorDescriptions properties, plus a customUrls key if custom URLs
|
||||||
// have been configured for the job.
|
// have been configured for the job.
|
||||||
this.getBasicJobInfo = function () {
|
getBasicJobInfo() {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, jobs: [] };
|
const obj = { success: true, jobs: [] };
|
||||||
|
|
||||||
ml.jobs()
|
ml.jobs()
|
||||||
|
@ -628,15 +609,15 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
if (resp.jobs && resp.jobs.length > 0) {
|
if (resp.jobs && resp.jobs.length > 0) {
|
||||||
obj.jobs = processBasicJobInfo(this, resp.jobs);
|
obj.jobs = processBasicJobInfo(this, resp.jobs);
|
||||||
}
|
}
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
console.log('getBasicJobInfo error getting list of jobs:', resp);
|
console.log('getBasicJobInfo error getting list of jobs:', resp);
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains the list of fields by which record level results may be viewed for all
|
// Obtains the list of fields by which record level results may be viewed for all
|
||||||
// the jobs that have been created. Essentially this is the list of unique 'by',
|
// the jobs that have been created. Essentially this is the list of unique 'by',
|
||||||
|
@ -648,8 +629,8 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
// for that job.
|
// for that job.
|
||||||
// Contains an addition '*' key which holds an array of the
|
// Contains an addition '*' key which holds an array of the
|
||||||
// unique fields across all jobs.
|
// unique fields across all jobs.
|
||||||
this.getJobViewByFields = function () {
|
getJobViewByFields() {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, fieldsByJob: { '*': [] } };
|
const obj = { success: true, fieldsByJob: { '*': [] } };
|
||||||
|
|
||||||
ml.jobs()
|
ml.jobs()
|
||||||
|
@ -688,20 +669,20 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
console.log('getJobViewByFields error getting list of viewBy fields:', resp);
|
console.log('getJobViewByFields error getting list of viewBy fields:', resp);
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// search to load a few records to extract the time field
|
// search to load a few records to extract the time field
|
||||||
this.searchTimeFields = function (index, type, field) {
|
searchTimeFields(index, type, field) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = { time: '' };
|
const obj = { time: '' };
|
||||||
|
|
||||||
es.search({
|
es.search({
|
||||||
|
@ -718,16 +699,16 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
obj.time = hit._source[field];
|
obj.time = hit._source[field];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.searchPreview = function (job) {
|
searchPreview(job) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
if (job.datafeed_config) {
|
if (job.datafeed_config) {
|
||||||
|
|
||||||
|
@ -749,11 +730,11 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
// This time filter is required for datafeed searches using aggregations to ensure
|
// This time filter is required for datafeed searches using aggregations to ensure
|
||||||
// the search does not create too many buckets (default 10000 max_bucket limit),
|
// the search does not create too many buckets (default 10000 max_bucket limit),
|
||||||
// but apply it to searches without aggregations too for consistency.
|
// but apply it to searches without aggregations too for consistency.
|
||||||
const fieldsService = Private(FieldsServiceProvider);
|
ml.getTimeFieldRange({
|
||||||
fieldsService.getTimeFieldRange(
|
index: job.datafeed_config.indices,
|
||||||
job.datafeed_config.indices,
|
timeFieldName: job.data_description.time_field,
|
||||||
job.data_description.time_field,
|
query
|
||||||
query)
|
})
|
||||||
.then((timeRange) => {
|
.then((timeRange) => {
|
||||||
const bucketSpan = parseInterval(job.analysis_config.bucket_span);
|
const bucketSpan = parseInterval(job.analysis_config.bucket_span);
|
||||||
const earliestMs = timeRange.start.epoch;
|
const earliestMs = timeRange.start.epoch;
|
||||||
|
@ -856,67 +837,68 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
|
|
||||||
es.search(data)
|
es.search(data)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
deferred.resolve(resp);
|
resolve(resp);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.openJob = function (jobId) {
|
openJob(jobId) {
|
||||||
return ml.openJob({ jobId });
|
return $q.when(ml.openJob({ jobId }));
|
||||||
};
|
}
|
||||||
|
|
||||||
this.closeJob = function (jobId) {
|
closeJob(jobId) {
|
||||||
return ml.closeJob({ jobId });
|
return $q.when(ml.closeJob({ jobId }));
|
||||||
};
|
}
|
||||||
|
|
||||||
this.forceCloseJob = function (jobId) {
|
forceCloseJob(jobId) {
|
||||||
return ml.forceCloseJob({ jobId });
|
return $q.when(ml.forceCloseJob({ jobId }));
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
this.saveNewDatafeed = function (datafeedConfig, jobId) {
|
saveNewDatafeed(datafeedConfig, jobId) {
|
||||||
const datafeedId = 'datafeed-' + jobId;
|
const datafeedId = `datafeed-${jobId}`;
|
||||||
datafeedConfig.job_id = jobId;
|
datafeedConfig.job_id = jobId;
|
||||||
|
|
||||||
return ml.addDatafeed({
|
return $q.when(ml.addDatafeed({
|
||||||
datafeedId,
|
datafeedId,
|
||||||
datafeedConfig
|
datafeedConfig
|
||||||
});
|
}));
|
||||||
};
|
}
|
||||||
|
|
||||||
this.updateDatafeed = function (datafeedId, datafeedConfig) {
|
updateDatafeed(datafeedId, datafeedConfig) {
|
||||||
return ml.updateDatafeed({ datafeedId, datafeedConfig })
|
return $q.when(ml.updateDatafeed({ datafeedId, datafeedConfig }))
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
console.log('update datafeed', resp);
|
console.log('update datafeed', resp);
|
||||||
return { success: true };
|
return { success: true };
|
||||||
}).catch((err) => {
|
})
|
||||||
|
.catch((err) => {
|
||||||
msgs.error('Could not update datafeed: ' + datafeedId);
|
msgs.error('Could not update datafeed: ' + datafeedId);
|
||||||
console.log('update datafeed', err);
|
console.log('update datafeed', err);
|
||||||
return { success: false, message: err.message };
|
return { success: false, message: err.message };
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.deleteDatafeed = function () {
|
deleteDatafeed() {
|
||||||
|
|
||||||
};
|
}
|
||||||
|
|
||||||
// start the datafeed for a given job
|
// start the datafeed for a given job
|
||||||
// refresh the job state on start success
|
// refresh the job state on start success
|
||||||
this.startDatafeed = function (datafeedId, jobId, start, end) {
|
startDatafeed(datafeedId, jobId, start, end) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
|
|
||||||
// if the end timestamp is a number, add one ms to it to make it
|
// if the end timestamp is a number, add one ms to it to make it
|
||||||
// inclusive of the end of the data
|
// inclusive of the end of the data
|
||||||
|
@ -930,74 +912,116 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
deferred.resolve(resp);
|
resolve(resp);
|
||||||
|
})
|
||||||
}).catch((err) => {
|
.catch((err) => {
|
||||||
console.log('MlJobsList error starting datafeed:', err);
|
console.log('jobService error starting datafeed:', err);
|
||||||
msgs.error('Could not start datafeed for ' + jobId, err);
|
msgs.error('Could not start datafeed for ' + jobId, err);
|
||||||
deferred.reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// stop the datafeed for a given job
|
// stop the datafeed for a given job
|
||||||
// refresh the job state on stop success
|
// refresh the job state on stop success
|
||||||
this.stopDatafeed = function (datafeedId, jobId) {
|
stopDatafeed(datafeedId, jobId) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
ml.stopDatafeed({
|
ml.stopDatafeed({
|
||||||
datafeedId
|
datafeedId
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
deferred.resolve(resp);
|
resolve(resp);
|
||||||
|
})
|
||||||
}).catch((err) => {
|
.catch((err) => {
|
||||||
console.log('MlJobsList error stopping datafeed:', err);
|
console.log('jobService error stopping datafeed:', err);
|
||||||
if (err.statusCode === 500) {
|
if (err.statusCode === 500) {
|
||||||
msgs.error('Could not stop datafeed for ' + jobId);
|
msgs.error('Could not stop datafeed for ' + jobId);
|
||||||
msgs.error('Request may have timed out and may still be running in the background.');
|
msgs.error('Request may have timed out and may still be running in the background.');
|
||||||
} else {
|
} else {
|
||||||
msgs.error('Could not stop datafeed for ' + jobId, err);
|
msgs.error('Could not stop datafeed for ' + jobId, err);
|
||||||
}
|
}
|
||||||
deferred.reject(err);
|
reject(err);
|
||||||
});
|
});
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.validateDetector = function (detector) {
|
validateDetector(detector) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
if (detector) {
|
if (detector) {
|
||||||
ml.validateDetector({ detector })
|
ml.validateDetector({ detector })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
deferred.resolve(resp);
|
resolve(resp);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
deferred.reject({});
|
reject({});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
this.getDatafeedId = function (jobId) {
|
getDatafeedId(jobId) {
|
||||||
let datafeedId = datafeedIds[jobId];
|
let datafeedId = datafeedIds[jobId];
|
||||||
if (datafeedId === undefined) {
|
if (datafeedId === undefined) {
|
||||||
datafeedId = 'datafeed-' + jobId;
|
datafeedId = `datafeed-${jobId}`;
|
||||||
}
|
}
|
||||||
return datafeedId;
|
return datafeedId;
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getDatafeedPreview = function (jobId) {
|
getDatafeedPreview(jobId) {
|
||||||
const datafeedId = this.getDatafeedId(jobId);
|
const datafeedId = this.getDatafeedId(jobId);
|
||||||
return ml.datafeedPreview({ datafeedId });
|
return $q.when(ml.datafeedPreview({ datafeedId }));
|
||||||
};
|
}
|
||||||
|
|
||||||
function processBasicJobInfo(mlJobService, jobsList) {
|
// 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
// Process the list of job data obtained from the jobs endpoint to return
|
// Process the list of job data obtained from the jobs endpoint to return
|
||||||
// an array of objects containing the basic information (id, description, bucketSpan, detectors
|
// an array of objects containing the basic information (id, description, bucketSpan, detectors
|
||||||
// and detectorDescriptions properties, plus a customUrls key if custom URLs
|
// and detectorDescriptions properties, plus a customUrls key if custom URLs
|
||||||
// have been configured for the job) used by various result dashboards in the ml plugin.
|
// have been configured for the job) used by various result dashboards in the ml plugin.
|
||||||
// The key information is stored in the mlJobService object for quick access.
|
// The key information is stored in the jobService object for quick access.
|
||||||
const processedJobsList = [];
|
const processedJobsList = [];
|
||||||
let detectorDescriptionsByJob = {};
|
let detectorDescriptionsByJob = {};
|
||||||
const detectorsByJob = {};
|
const detectorsByJob = {};
|
||||||
|
@ -1047,10 +1071,10 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mlJobService.jobDescriptions[job.id] = job.description;
|
localJobService.jobDescriptions[job.id] = job.description;
|
||||||
detectorDescriptionsByJob[job.id] = job.detectorDescriptions;
|
detectorDescriptionsByJob[job.id] = job.detectorDescriptions;
|
||||||
detectorsByJob[job.id] = job.detectors;
|
detectorsByJob[job.id] = job.detectors;
|
||||||
mlJobService.basicJobs[job.id] = job;
|
localJobService.basicJobs[job.id] = job;
|
||||||
processedJobsList.push(job);
|
processedJobsList.push(job);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1060,8 +1084,8 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
dtr.detector_description = detectorDescriptionsByJob[jobId][i];
|
dtr.detector_description = detectorDescriptionsByJob[jobId][i];
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
mlJobService.detectorsByJob = detectorsByJob;
|
localJobService.detectorsByJob = detectorsByJob;
|
||||||
mlJobService.customUrlsByJob = customUrlsByJob;
|
localJobService.customUrlsByJob = customUrlsByJob;
|
||||||
|
|
||||||
return processedJobsList;
|
return processedJobsList;
|
||||||
}
|
}
|
||||||
|
@ -1131,25 +1155,8 @@ module.service('mlJobService', function ($rootScope, $http, $q, es, ml, mlMessag
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the list of job group ids as well as how many jobs are in each group
|
if (jobService === undefined) {
|
||||||
this.getJobGroups = function () {
|
jobService = new JobService();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
});
|
return jobService;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
_.each(tempGroups, (js, id) => {
|
|
||||||
groups.push({ id, jobs: js });
|
|
||||||
});
|
|
||||||
return groups;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
|
@ -8,15 +8,12 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlESMappingService', function ($q, ml) {
|
// Returns the mapping type of the specified field.
|
||||||
|
// Accepts fieldName containing dots representing a nested sub-field.
|
||||||
// Returns the mapping type of the specified field.
|
export function getFieldTypeFromMapping(index, fieldName) {
|
||||||
// Accepts fieldName containing dots representing a nested sub-field.
|
return new Promise((resolve, reject) => {
|
||||||
this.getFieldTypeFromMapping = function (index, fieldName) {
|
|
||||||
return $q((resolve, reject) => {
|
|
||||||
if (index !== '') {
|
if (index !== '') {
|
||||||
ml.getFieldCaps({ index, fields: [fieldName] })
|
ml.getFieldCaps({ index, fields: [fieldName] })
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
@ -37,5 +34,4 @@ module.service('mlESMappingService', function ($q, ml) {
|
||||||
reject();
|
reject();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
});
|
|
||||||
|
|
|
@ -7,142 +7,139 @@
|
||||||
|
|
||||||
|
|
||||||
import { pick } from 'lodash';
|
import { pick } from 'lodash';
|
||||||
import './http_service';
|
|
||||||
import chrome from 'ui/chrome';
|
import chrome from 'ui/chrome';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { http } from './http_service';
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('ml', function (prlHttpService) {
|
const basePath = chrome.addBasePath('/api/ml');
|
||||||
const http = prlHttpService;
|
|
||||||
const basePath = chrome.addBasePath('/api/ml');
|
|
||||||
|
|
||||||
this.jobs = function (obj) {
|
export const ml = {
|
||||||
|
jobs(obj) {
|
||||||
const jobId = (obj && obj.jobId) ? `/${obj.jobId}` : '';
|
const jobId = (obj && obj.jobId) ? `/${obj.jobId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors${jobId}`,
|
url: `${basePath}/anomaly_detectors${jobId}`,
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.jobStats = function (obj) {
|
jobStats(obj) {
|
||||||
const jobId = (obj && obj.jobId) ? `/${obj.jobId}` : '';
|
const jobId = (obj && obj.jobId) ? `/${obj.jobId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors${jobId}/_stats`,
|
url: `${basePath}/anomaly_detectors${jobId}/_stats`,
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.addJob = function (obj) {
|
addJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: obj.job
|
data: obj.job
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.openJob = function (obj) {
|
openJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/_open`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/_open`,
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.closeJob = function (obj) {
|
closeJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/_close`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/_close`,
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.forceCloseJob = function (obj) {
|
forceCloseJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/_close?force=true`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/_close?force=true`,
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.deleteJob = function (obj) {
|
deleteJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.forceDeleteJob = function (obj) {
|
forceDeleteJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}?force=true`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}?force=true`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.updateJob = function (obj) {
|
updateJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/_update`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/_update`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj.job
|
data: obj.job
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.estimateBucketSpan = function (obj) {
|
estimateBucketSpan(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/validate/estimate_bucket_span`,
|
url: `${basePath}/validate/estimate_bucket_span`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj
|
data: obj
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.validateJob = function (obj) {
|
validateJob(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/validate/job`,
|
url: `${basePath}/validate/job`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj
|
data: obj
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.datafeeds = function (obj) {
|
datafeeds(obj) {
|
||||||
const datafeedId = (obj && obj.datafeedId) ? `/${obj.datafeedId}` : '';
|
const datafeedId = (obj && obj.datafeedId) ? `/${obj.datafeedId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds${datafeedId}`,
|
url: `${basePath}/datafeeds${datafeedId}`,
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.datafeedStats = function (obj) {
|
datafeedStats(obj) {
|
||||||
const datafeedId = (obj && obj.datafeedId) ? `/${obj.datafeedId}` : '';
|
const datafeedId = (obj && obj.datafeedId) ? `/${obj.datafeedId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds${datafeedId}/_stats`,
|
url: `${basePath}/datafeeds${datafeedId}/_stats`,
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.addDatafeed = function (obj) {
|
addDatafeed(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: obj.datafeedConfig
|
data: obj.datafeedConfig
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.updateDatafeed = function (obj) {
|
updateDatafeed(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}/_update`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}/_update`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj.datafeedConfig
|
data: obj.datafeedConfig
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.deleteDatafeed = function (obj) {
|
deleteDatafeed(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.forceDeleteDatafeed = function (obj) {
|
forceDeleteDatafeed(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}?force=true`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}?force=true`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.startDatafeed = function (obj) {
|
startDatafeed(obj) {
|
||||||
const data = {};
|
const data = {};
|
||||||
if(obj.start !== undefined) {
|
if(obj.start !== undefined) {
|
||||||
data.start = obj.start;
|
data.start = obj.start;
|
||||||
|
@ -150,78 +147,78 @@ module.service('ml', function (prlHttpService) {
|
||||||
if(obj.end !== undefined) {
|
if(obj.end !== undefined) {
|
||||||
data.end = obj.end;
|
data.end = obj.end;
|
||||||
}
|
}
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}/_start`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}/_start`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.stopDatafeed = function (obj) {
|
stopDatafeed(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}/_stop`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}/_stop`,
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.datafeedPreview = function (obj) {
|
datafeedPreview(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/datafeeds/${obj.datafeedId}/_preview`,
|
url: `${basePath}/datafeeds/${obj.datafeedId}/_preview`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.validateDetector = function (obj) {
|
validateDetector(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/_validate/detector`,
|
url: `${basePath}/anomaly_detectors/_validate/detector`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj.detector
|
data: obj.detector
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.forecast = function (obj) {
|
forecast(obj) {
|
||||||
const data = {};
|
const data = {};
|
||||||
if(obj.duration !== undefined) {
|
if(obj.duration !== undefined) {
|
||||||
data.duration = obj.duration;
|
data.duration = obj.duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/_forecast`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/_forecast`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.overallBuckets = function (obj) {
|
overallBuckets(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'topN',
|
'topN',
|
||||||
'bucketSpan',
|
'bucketSpan',
|
||||||
'start',
|
'start',
|
||||||
'end'
|
'end'
|
||||||
]);
|
]);
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/anomaly_detectors/${obj.jobId}/results/overall_buckets`,
|
url: `${basePath}/anomaly_detectors/${obj.jobId}/results/overall_buckets`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.checkPrivilege = function (obj) {
|
checkPrivilege(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/_has_privileges`,
|
url: `${basePath}/_has_privileges`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data: obj
|
data: obj
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getNotificationSettings = function () {
|
getNotificationSettings() {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/notification_settings`,
|
url: `${basePath}/notification_settings`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getFieldCaps = function (obj) {
|
getFieldCaps(obj) {
|
||||||
const data = {};
|
const data = {};
|
||||||
if(obj.index !== undefined) {
|
if(obj.index !== undefined) {
|
||||||
data.index = obj.index;
|
data.index = obj.index;
|
||||||
|
@ -229,28 +226,28 @@ module.service('ml', function (prlHttpService) {
|
||||||
if(obj.fields !== undefined) {
|
if(obj.fields !== undefined) {
|
||||||
data.fields = obj.fields;
|
data.fields = obj.fields;
|
||||||
}
|
}
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/indices/field_caps`,
|
url: `${basePath}/indices/field_caps`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.recognizeIndex = function (obj) {
|
recognizeIndex(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/modules/recognize/${obj.indexPatternTitle}`,
|
url: `${basePath}/modules/recognize/${obj.indexPatternTitle}`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getDataRecognizerModule = function (obj) {
|
getDataRecognizerModule(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/modules/get_module/${obj.moduleId}`,
|
url: `${basePath}/modules/get_module/${obj.moduleId}`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.setupDataRecognizerConfig = function (obj) {
|
setupDataRecognizerConfig(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'prefix',
|
'prefix',
|
||||||
'groups',
|
'groups',
|
||||||
|
@ -258,14 +255,14 @@ module.service('ml', function (prlHttpService) {
|
||||||
'query'
|
'query'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/modules/setup/${obj.moduleId}`,
|
url: `${basePath}/modules/setup/${obj.moduleId}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getVisualizerFieldStats = function (obj) {
|
getVisualizerFieldStats(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'query',
|
'query',
|
||||||
'timeFieldName',
|
'timeFieldName',
|
||||||
|
@ -277,14 +274,14 @@ module.service('ml', function (prlHttpService) {
|
||||||
'maxExamples'
|
'maxExamples'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/data_visualizer/get_field_stats/${obj.indexPatternTitle}`,
|
url: `${basePath}/data_visualizer/get_field_stats/${obj.indexPatternTitle}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getVisualizerOverallStats = function (obj) {
|
getVisualizerOverallStats(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'query',
|
'query',
|
||||||
'timeFieldName',
|
'timeFieldName',
|
||||||
|
@ -295,61 +292,60 @@ module.service('ml', function (prlHttpService) {
|
||||||
'nonAggregatableFields'
|
'nonAggregatableFields'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/data_visualizer/get_overall_stats/${obj.indexPatternTitle}`,
|
url: `${basePath}/data_visualizer/get_overall_stats/${obj.indexPatternTitle}`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.calendars = function (obj) {
|
calendars(obj) {
|
||||||
const calendarId = (obj && obj.calendarId) ? `/${obj.calendarId}` : '';
|
const calendarId = (obj && obj.calendarId) ? `/${obj.calendarId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/calendars${calendarId}`,
|
url: `${basePath}/calendars${calendarId}`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
|
addCalendar(obj) {
|
||||||
this.addCalendar = function (obj) {
|
return http({
|
||||||
return http.request({
|
|
||||||
url: `${basePath}/calendars`,
|
url: `${basePath}/calendars`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: obj
|
data: obj
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.updateCalendar = function (obj) {
|
updateCalendar(obj) {
|
||||||
const calendarId = (obj && obj.calendarId) ? `/${obj.calendarId}` : '';
|
const calendarId = (obj && obj.calendarId) ? `/${obj.calendarId}` : '';
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/calendars${calendarId}`,
|
url: `${basePath}/calendars${calendarId}`,
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
data: obj
|
data: obj
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.deleteCalendar = function (obj) {
|
deleteCalendar(obj) {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/calendars/${obj.calendarId}`,
|
url: `${basePath}/calendars/${obj.calendarId}`,
|
||||||
method: 'DELETE'
|
method: 'DELETE'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.mlNodeCount = function () {
|
mlNodeCount() {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/ml_node_count`,
|
url: `${basePath}/ml_node_count`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.mlInfo = function () {
|
mlInfo() {
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/info`,
|
url: `${basePath}/info`,
|
||||||
method: 'GET'
|
method: 'GET'
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.calculateModelMemoryLimit = function (obj) {
|
calculateModelMemoryLimit(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'indexPattern',
|
'indexPattern',
|
||||||
'splitFieldName',
|
'splitFieldName',
|
||||||
|
@ -361,14 +357,14 @@ module.service('ml', function (prlHttpService) {
|
||||||
'latestMs'
|
'latestMs'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/validate/calculate_model_memory_limit`,
|
url: `${basePath}/validate/calculate_model_memory_limit`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getCardinalityOfFields = function (obj) {
|
getCardinalityOfFields(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'index',
|
'index',
|
||||||
'types',
|
'types',
|
||||||
|
@ -379,25 +375,24 @@ module.service('ml', function (prlHttpService) {
|
||||||
'latestMs'
|
'latestMs'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/fields_service/field_cardinality`,
|
url: `${basePath}/fields_service/field_cardinality`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
},
|
||||||
|
|
||||||
this.getTimeFieldRange = function (obj) {
|
getTimeFieldRange(obj) {
|
||||||
const data = pick(obj, [
|
const data = pick(obj, [
|
||||||
'index',
|
'index',
|
||||||
'timeFieldName',
|
'timeFieldName',
|
||||||
'query'
|
'query'
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return http.request({
|
return http({
|
||||||
url: `${basePath}/fields_service/time_field_range`,
|
url: `${basePath}/fields_service/time_field_range`,
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
data
|
data
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
};
|
||||||
});
|
|
||||||
|
|
|
@ -1,52 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// service for copying text to the users clipboard
|
|
||||||
// can only work when triggered via a user event, as part of an onclick or ng-click
|
|
||||||
// returns success
|
|
||||||
// e.g. mlClipboardService.copy("this could be abused!");
|
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlClipboardService', function () {
|
|
||||||
|
|
||||||
function copyTextToClipboard(text) {
|
|
||||||
const textArea = document.createElement('textarea');
|
|
||||||
textArea.style.position = 'fixed';
|
|
||||||
textArea.style.top = 0;
|
|
||||||
textArea.style.left = 0;
|
|
||||||
textArea.style.width = '2em';
|
|
||||||
textArea.style.height = '2em';
|
|
||||||
textArea.style.padding = 0;
|
|
||||||
textArea.style.border = 'none';
|
|
||||||
textArea.style.outline = 'none';
|
|
||||||
textArea.style.boxShadow = 'none';
|
|
||||||
textArea.style.background = 'transparent';
|
|
||||||
textArea.value = text;
|
|
||||||
|
|
||||||
document.body.appendChild(textArea);
|
|
||||||
|
|
||||||
textArea.select();
|
|
||||||
|
|
||||||
let successful = false;
|
|
||||||
try {
|
|
||||||
successful = document.execCommand('copy');
|
|
||||||
const msg = successful ? 'successful' : 'unsuccessful';
|
|
||||||
console.log('Copying text command was ' + msg);
|
|
||||||
} catch (err) {
|
|
||||||
console.log('Oops, unable to copy');
|
|
||||||
}
|
|
||||||
|
|
||||||
document.body.removeChild(textArea);
|
|
||||||
return successful;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.copy = copyTextToClipboard;
|
|
||||||
});
|
|
||||||
|
|
|
@ -1,176 +0,0 @@
|
||||||
/*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Service for carrying out Elasticsearch queries to obtain data for the
|
|
||||||
// Ml Results dashboards.
|
|
||||||
import _ from 'lodash';
|
|
||||||
|
|
||||||
import { ML_NOTIFICATION_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlNotificationService', function ($q, es) {
|
|
||||||
|
|
||||||
// search for audit messages, jobId is optional.
|
|
||||||
// without it, all jobs will be listed.
|
|
||||||
// fromRange should be a string formatted in ES time units. e.g. 12h, 1d, 7d
|
|
||||||
this.getJobAuditMessages = function (fromRange, jobId) {
|
|
||||||
const deferred = $q.defer();
|
|
||||||
const messages = [];
|
|
||||||
|
|
||||||
let jobFilter = {};
|
|
||||||
// if no jobId specified, load all of the messages
|
|
||||||
if (jobId !== undefined) {
|
|
||||||
jobFilter = {
|
|
||||||
'bool': {
|
|
||||||
'should': [
|
|
||||||
{
|
|
||||||
'term': {
|
|
||||||
'job_id': '' // catch system messages
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
'term': {
|
|
||||||
'job_id': jobId // messages for specified jobId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
let timeFilter = {};
|
|
||||||
if (fromRange !== undefined && fromRange !== '') {
|
|
||||||
timeFilter = {
|
|
||||||
'range': {
|
|
||||||
'timestamp': {
|
|
||||||
'gte': 'now-' + fromRange,
|
|
||||||
'lte': 'now'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
es.search({
|
|
||||||
index: ML_NOTIFICATION_INDEX_PATTERN,
|
|
||||||
ignore_unavailable: true,
|
|
||||||
size: 1000,
|
|
||||||
body:
|
|
||||||
{
|
|
||||||
sort: [
|
|
||||||
{ 'timestamp': { 'order': 'asc' } },
|
|
||||||
{ 'job_id': { 'order': 'asc' } }
|
|
||||||
],
|
|
||||||
'query': {
|
|
||||||
'bool': {
|
|
||||||
'filter': [
|
|
||||||
{
|
|
||||||
'bool': {
|
|
||||||
'must_not': {
|
|
||||||
'term': {
|
|
||||||
'level': 'activity'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
jobFilter,
|
|
||||||
timeFilter
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.hits.total !== 0) {
|
|
||||||
_.each(resp.hits.hits, (hit) => {
|
|
||||||
messages.push(hit._source);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
deferred.resolve({ messages });
|
|
||||||
})
|
|
||||||
.catch((resp) => {
|
|
||||||
deferred.reject(resp);
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
// search highest, most recent audit messages for all jobs for the last 24hrs.
|
|
||||||
this.getAuditMessagesSummary = function () {
|
|
||||||
const deferred = $q.defer();
|
|
||||||
const aggs = [];
|
|
||||||
|
|
||||||
es.search({
|
|
||||||
index: ML_NOTIFICATION_INDEX_PATTERN,
|
|
||||||
ignore_unavailable: true,
|
|
||||||
size: 0,
|
|
||||||
body: {
|
|
||||||
'query': {
|
|
||||||
'bool': {
|
|
||||||
'filter': {
|
|
||||||
'range': {
|
|
||||||
'timestamp': {
|
|
||||||
'gte': 'now-1d'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'aggs': {
|
|
||||||
'levelsPerJob': {
|
|
||||||
'terms': {
|
|
||||||
'field': 'job_id',
|
|
||||||
},
|
|
||||||
'aggs': {
|
|
||||||
'levels': {
|
|
||||||
'terms': {
|
|
||||||
'field': 'level',
|
|
||||||
},
|
|
||||||
'aggs': {
|
|
||||||
'latestMessage': {
|
|
||||||
'terms': {
|
|
||||||
'field': 'message.raw',
|
|
||||||
'size': 1,
|
|
||||||
'order': {
|
|
||||||
'latestMessage': 'desc'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
'aggs': {
|
|
||||||
'latestMessage': {
|
|
||||||
'max': {
|
|
||||||
'field': 'timestamp'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then((resp) => {
|
|
||||||
if (resp.hits.total !== 0 &&
|
|
||||||
resp.aggregations &&
|
|
||||||
resp.aggregations.levelsPerJob &&
|
|
||||||
resp.aggregations.levelsPerJob.buckets &&
|
|
||||||
resp.aggregations.levelsPerJob.buckets.length) {
|
|
||||||
_.each(resp.aggregations.levelsPerJob.buckets, (agg) => {
|
|
||||||
aggs.push(agg);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
deferred.resolve({ messagesPerJob: aggs });
|
|
||||||
})
|
|
||||||
.catch((resp) => {
|
|
||||||
deferred.reject(resp);
|
|
||||||
});
|
|
||||||
return deferred.promise;
|
|
||||||
};
|
|
||||||
|
|
||||||
});
|
|
|
@ -14,16 +14,14 @@ import { ML_MEDIAN_PERCENTS } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { escapeForElasticsearchQuery } from 'plugins/ml/util/string_utils';
|
import { escapeForElasticsearchQuery } from 'plugins/ml/util/string_utils';
|
||||||
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
import { ML_RESULTS_INDEX_PATTERN } from 'plugins/ml/constants/index_patterns';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
const module = uiModules.get('apps/ml');
|
|
||||||
|
|
||||||
module.service('mlResultsService', function ($q, es, ml) {
|
export function ResultsServiceProvider($q, es) {
|
||||||
|
// Obtains the maximum bucket anomaly scores by job ID and time.
|
||||||
// Obtains the maximum bucket anomaly scores by job ID and time.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Returned response contains a results property, with a key for job
|
||||||
// Returned response contains a results property, with a key for job
|
// which has results for the specified time range.
|
||||||
// which has results for the specified time range.
|
function getScoresByBucket(jobIds, earliestMs, latestMs, interval, maxResults) {
|
||||||
this.getScoresByBucket = function (jobIds, earliestMs, latestMs, interval, maxResults) {
|
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -141,13 +139,13 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains a list of scheduled events by job ID and time.
|
// Obtains a list of scheduled events by job ID and time.
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Returned response contains a events property, which will only
|
// Returned response contains a events property, which will only
|
||||||
// contains keys for jobs which have scheduled events for the specified time range.
|
// contains keys for jobs which have scheduled events for the specified time range.
|
||||||
this.getScheduledEventsByBucket = function (
|
function getScheduledEventsByBucket(
|
||||||
jobIds,
|
jobIds,
|
||||||
earliestMs,
|
earliestMs,
|
||||||
latestMs,
|
latestMs,
|
||||||
|
@ -256,14 +254,14 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Obtains the top influencers, by maximum influencer score, for the specified index, time range and job ID(s).
|
// Obtains the top influencers, by maximum influencer score, for the specified index, time range and job ID(s).
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Returned response contains an influencers property, with a key for each of the influencer field names,
|
// Returned response contains an influencers property, with a key for each of the influencer field names,
|
||||||
// whose value is an array of objects containing influencerFieldValue, maxAnomalyScore and sumAnomalyScore keys.
|
// whose value is an array of objects containing influencerFieldValue, maxAnomalyScore and sumAnomalyScore keys.
|
||||||
this.getTopInfluencers = function (jobIds, earliestMs, latestMs, maxFieldNames, maxFieldValues) {
|
function getTopInfluencers(jobIds, earliestMs, latestMs, maxFieldNames, maxFieldValues) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, influencers: {} };
|
const obj = { success: true, influencers: {} };
|
||||||
|
|
||||||
|
@ -385,21 +383,20 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains the top influencer field values, by maximum anomaly score, for a
|
// Obtains the top influencer field values, by maximum anomaly score, for a
|
||||||
// particular index, field name and job ID(s).
|
// particular index, field name and job ID(s).
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Returned response contains a results property, which is an array of objects
|
// Returned response contains a results property, which is an array of objects
|
||||||
// containing influencerFieldValue, maxAnomalyScore and sumAnomalyScore keys.
|
// containing influencerFieldValue, maxAnomalyScore and sumAnomalyScore keys.
|
||||||
this.getTopInfluencerValues = function (jobIds, influencerFieldName, earliestMs, latestMs, maxResults) {
|
function getTopInfluencerValues(jobIds, influencerFieldName, earliestMs, latestMs, maxResults) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, results: [] };
|
const obj = { success: true, results: [] };
|
||||||
|
|
||||||
// Build the criteria to use in the bool filter part of the request.
|
// Build the criteria to use in the bool filter part of the request.
|
||||||
// Adds criteria for the time range plus any specified job IDs.
|
// Adds criteria for the time range plus any specified job IDs.
|
||||||
const boolCriteria = [];
|
const boolCriteria = [{
|
||||||
boolCriteria.push({
|
|
||||||
range: {
|
range: {
|
||||||
timestamp: {
|
timestamp: {
|
||||||
gte: earliestMs,
|
gte: earliestMs,
|
||||||
|
@ -407,7 +404,8 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
format: 'epoch_millis'
|
format: 'epoch_millis'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
}];
|
||||||
|
|
||||||
if (jobIds && jobIds.length > 0 && !(jobIds.length === 1 && jobIds[0] === '*')) {
|
if (jobIds && jobIds.length > 0 && !(jobIds.length === 1 && jobIds[0] === '*')) {
|
||||||
let jobIdFilterStr = '';
|
let jobIdFilterStr = '';
|
||||||
_.each(jobIds, (jobId, i) => {
|
_.each(jobIds, (jobId, i) => {
|
||||||
|
@ -487,12 +485,12 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains the overall bucket scores for the specified job ID(s).
|
// Obtains the overall bucket scores for the specified job ID(s).
|
||||||
// Pass ['*'] to search over all job IDs.
|
// Pass ['*'] to search over all job IDs.
|
||||||
// Returned response contains a results property as an object of max score by time.
|
// Returned response contains a results property as an object of max score by time.
|
||||||
this.getOverallBucketScores = function (jobIds, topN, earliestMs, latestMs, interval) {
|
function getOverallBucketScores(jobIds, topN, earliestMs, latestMs, interval) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, results: {} };
|
const obj = { success: true, results: {} };
|
||||||
|
|
||||||
|
@ -518,14 +516,14 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Obtains the maximum score by influencer_field_value and by time for the specified job ID(s)
|
// Obtains the maximum score by influencer_field_value and by time for the specified job ID(s)
|
||||||
// (pass an empty array or ['*'] to search over all job IDs), and specified influencer field
|
// (pass an empty array or ['*'] to search over all job IDs), and specified influencer field
|
||||||
// values (pass an empty array to search over all field values).
|
// values (pass an empty array to search over all field values).
|
||||||
// Returned response contains a results property with influencer field values keyed
|
// Returned response contains a results property with influencer field values keyed
|
||||||
// against max score by time.
|
// against max score by time.
|
||||||
this.getInfluencerValueMaxScoreByTime = function (
|
function getInfluencerValueMaxScoreByTime(
|
||||||
jobIds,
|
jobIds,
|
||||||
influencerFieldName,
|
influencerFieldName,
|
||||||
influencerFieldValues,
|
influencerFieldValues,
|
||||||
|
@ -666,13 +664,13 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Obtains the definition of the category with the specified ID and job ID.
|
// Obtains the definition of the category with the specified ID and job ID.
|
||||||
// Returned response contains four properties - categoryId, regex, examples
|
// Returned response contains four properties - categoryId, regex, examples
|
||||||
// and terms (space delimited String of the common tokens matched in values of the category).
|
// and terms (space delimited String of the common tokens matched in values of the category).
|
||||||
this.getCategoryDefinition = function (jobId, categoryId) {
|
function getCategoryDefinition(jobId, categoryId) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, categoryId: categoryId, terms: null, regex: null, examples: [] };
|
const obj = { success: true, categoryId: categoryId, terms: null, regex: null, examples: [] };
|
||||||
|
|
||||||
|
@ -704,14 +702,14 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Obtains the categorization examples for the categories with the specified IDs
|
// Obtains the categorization examples for the categories with the specified IDs
|
||||||
// from the given index and job ID.
|
// from the given index and job ID.
|
||||||
// Returned response contains two properties - jobId and
|
// Returned response contains two properties - jobId and
|
||||||
// examplesByCategoryId (list of examples against categoryId).
|
// examplesByCategoryId (list of examples against categoryId).
|
||||||
this.getCategoryExamples = function (jobId, categoryIds, maxExamples) {
|
function getCategoryExamples(jobId, categoryIds, maxExamples) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, jobId: jobId, examplesByCategoryId: {} };
|
const obj = { success: true, jobId: jobId, examplesByCategoryId: {} };
|
||||||
|
|
||||||
|
@ -747,7 +745,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain record level results containing the influencers
|
// Queries Elasticsearch to obtain record level results containing the influencers
|
||||||
|
@ -755,7 +753,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Returned response contains a records property, with each record containing
|
// Returned response contains a records property, with each record containing
|
||||||
// only the fields job_id, detector_index, record_score and influencers.
|
// only the fields job_id, detector_index, record_score and influencers.
|
||||||
this.getRecordInfluencers = function (jobIds, threshold, earliestMs, latestMs, maxResults) {
|
function getRecordInfluencers(jobIds, threshold, earliestMs, latestMs, maxResults) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, records: [] };
|
const obj = { success: true, records: [] };
|
||||||
|
|
||||||
|
@ -851,7 +849,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain the record level results containing the specified influencer(s),
|
// Queries Elasticsearch to obtain the record level results containing the specified influencer(s),
|
||||||
|
@ -860,7 +858,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
// 'fieldValue' properties. The influencer array uses 'should' for the nested bool query,
|
// 'fieldValue' properties. The influencer array uses 'should' for the nested bool query,
|
||||||
// so this returns record level results which have at least one of the influencers.
|
// so this returns record level results which have at least one of the influencers.
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
this.getRecordsForInfluencer = function (jobIds, influencers, threshold, earliestMs, latestMs, maxResults) {
|
function getRecordsForInfluencer(jobIds, influencers, threshold, earliestMs, latestMs, maxResults) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, records: [] };
|
const obj = { success: true, records: [] };
|
||||||
|
|
||||||
|
@ -957,7 +955,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
},
|
},
|
||||||
sort: [
|
sort: [
|
||||||
{ record_score: { order: 'desc' } }
|
{ record_score: { order: 'desc' } }
|
||||||
],
|
]
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
|
@ -972,13 +970,13 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain the record level results for the specified job and detector,
|
// Queries Elasticsearch to obtain the record level results for the specified job and detector,
|
||||||
// time range, record score threshold, and whether to only return results containing influencers.
|
// time range, record score threshold, and whether to only return results containing influencers.
|
||||||
// An additional, optional influencer field name and value may also be provided.
|
// An additional, optional influencer field name and value may also be provided.
|
||||||
this.getRecordsForDetector = function (
|
function getRecordsForDetector(
|
||||||
jobId,
|
jobId,
|
||||||
detectorIndex,
|
detectorIndex,
|
||||||
checkForInfluencers,
|
checkForInfluencers,
|
||||||
|
@ -1098,22 +1096,22 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain all the record level results for the specified job(s), time range,
|
// Queries Elasticsearch to obtain all the record level results for the specified job(s), time range,
|
||||||
// and record score threshold.
|
// and record score threshold.
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
// Returned response contains a records property, which is an array of the matching results.
|
// Returned response contains a records property, which is an array of the matching results.
|
||||||
this.getRecords = function (jobIds, threshold, earliestMs, latestMs, maxResults) {
|
function getRecords(jobIds, threshold, earliestMs, latestMs, maxResults) {
|
||||||
return this.getRecordsForInfluencer(jobIds, [], threshold, earliestMs, latestMs, maxResults);
|
return this.getRecordsForInfluencer(jobIds, [], threshold, earliestMs, latestMs, maxResults);
|
||||||
};
|
}
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain the record level results matching the given criteria,
|
// Queries Elasticsearch to obtain the record level results matching the given criteria,
|
||||||
// for the specified job(s), time range, and record score threshold.
|
// for the specified job(s), time range, and record score threshold.
|
||||||
// criteriaFields parameter must be an array, with each object in the array having 'fieldName'
|
// criteriaFields parameter must be an array, with each object in the array having 'fieldName'
|
||||||
// 'fieldValue' properties.
|
// 'fieldValue' properties.
|
||||||
// Pass an empty array or ['*'] to search over all job IDs.
|
// Pass an empty array or ['*'] to search over all job IDs.
|
||||||
this.getRecordsForCriteria = function (jobIds, criteriaFields, threshold, earliestMs, latestMs, maxResults) {
|
function getRecordsForCriteria(jobIds, criteriaFields, threshold, earliestMs, latestMs, maxResults) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, records: [] };
|
const obj = { success: true, records: [] };
|
||||||
|
|
||||||
|
@ -1202,7 +1200,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain metric aggregation results.
|
// Queries Elasticsearch to obtain metric aggregation results.
|
||||||
|
@ -1213,7 +1211,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
// Extra query object can be supplied, or pass null if no additional query
|
// Extra query object can be supplied, or pass null if no additional query
|
||||||
// to that built from the supplied entity fields.
|
// to that built from the supplied entity fields.
|
||||||
// Returned response contains a results property containing the requested aggregation.
|
// Returned response contains a results property containing the requested aggregation.
|
||||||
this.getMetricData = function (
|
function getMetricData(
|
||||||
index,
|
index,
|
||||||
types,
|
types,
|
||||||
entityFields,
|
entityFields,
|
||||||
|
@ -1364,7 +1362,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain event rate data i.e. the count
|
// Queries Elasticsearch to obtain event rate data i.e. the count
|
||||||
// of documents over time.
|
// of documents over time.
|
||||||
|
@ -1372,7 +1370,7 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
// Extra query object can be supplied, or pass null if no additional query.
|
// Extra query object can be supplied, or pass null if no additional query.
|
||||||
// Returned response contains a results property, which is an object
|
// Returned response contains a results property, which is an object
|
||||||
// of document counts against time (epoch millis).
|
// of document counts against time (epoch millis).
|
||||||
this.getEventRateData = function (
|
function getEventRateData(
|
||||||
index,
|
index,
|
||||||
query,
|
query,
|
||||||
timeFieldName,
|
timeFieldName,
|
||||||
|
@ -1440,9 +1438,9 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
this.getModelPlotOutput = function (
|
function getModelPlotOutput(
|
||||||
jobId,
|
jobId,
|
||||||
detectorIndex,
|
detectorIndex,
|
||||||
criteriaFields,
|
criteriaFields,
|
||||||
|
@ -1584,13 +1582,13 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
}
|
||||||
|
|
||||||
// Queries Elasticsearch to obtain the max record score over time for the specified job,
|
// Queries Elasticsearch to obtain the max record score over time for the specified job,
|
||||||
// criteria, time range, and aggregation interval.
|
// criteria, time range, and aggregation interval.
|
||||||
// criteriaFields parameter must be an array, with each object in the array having 'fieldName'
|
// criteriaFields parameter must be an array, with each object in the array having 'fieldName'
|
||||||
// 'fieldValue' properties.
|
// 'fieldValue' properties.
|
||||||
this.getRecordMaxScoreByTime = function (jobId, criteriaFields, earliestMs, latestMs, interval) {
|
function getRecordMaxScoreByTime(jobId, criteriaFields, earliestMs, latestMs, interval) {
|
||||||
return $q((resolve, reject) => {
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
|
@ -1698,6 +1696,26 @@ module.service('mlResultsService', function ($q, es, ml) {
|
||||||
reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
getScoresByBucket,
|
||||||
|
getScheduledEventsByBucket,
|
||||||
|
getTopInfluencers,
|
||||||
|
getTopInfluencerValues,
|
||||||
|
getOverallBucketScores,
|
||||||
|
getInfluencerValueMaxScoreByTime,
|
||||||
|
getCategoryDefinition,
|
||||||
|
getCategoryExamples,
|
||||||
|
getRecordInfluencers,
|
||||||
|
getRecordsForInfluencer,
|
||||||
|
getRecordsForDetector,
|
||||||
|
getRecords,
|
||||||
|
getRecordsForCriteria,
|
||||||
|
getMetricData,
|
||||||
|
getEventRateData,
|
||||||
|
getModelPlotOutput,
|
||||||
|
getRecordMaxScoreByTime
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
}
|
||||||
|
|
|
@ -17,6 +17,7 @@ import { checkLicense } from 'plugins/ml/license/check_license';
|
||||||
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
|
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||||
import { getMlNodeCount, mlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { getMlNodeCount, mlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
import { buttonsEnabledChecks } from 'plugins/ml/settings/scheduled_events/calendars_list/buttons_enabled_checks';
|
import { buttonsEnabledChecks } from 'plugins/ml/settings/scheduled_events/calendars_list/buttons_enabled_checks';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import template from './calendars_list.html';
|
import template from './calendars_list.html';
|
||||||
|
|
||||||
|
@ -39,9 +40,9 @@ module.controller('MlCalendarsList',
|
||||||
$filter,
|
$filter,
|
||||||
$route,
|
$route,
|
||||||
$location,
|
$location,
|
||||||
|
$q,
|
||||||
pagerFactory,
|
pagerFactory,
|
||||||
Private,
|
Private,
|
||||||
ml,
|
|
||||||
timefilter,
|
timefilter,
|
||||||
mlConfirmModalService) {
|
mlConfirmModalService) {
|
||||||
|
|
||||||
|
@ -116,7 +117,7 @@ module.controller('MlCalendarsList',
|
||||||
title: `Delete calendar`
|
title: `Delete calendar`
|
||||||
})
|
})
|
||||||
.then(() => {
|
.then(() => {
|
||||||
ml.deleteCalendar({ calendarId })
|
$q.when(ml.deleteCalendar({ calendarId }))
|
||||||
.then(loadCalendars)
|
.then(loadCalendars)
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
console.log(error);
|
console.log(error);
|
||||||
|
@ -126,7 +127,7 @@ module.controller('MlCalendarsList',
|
||||||
};
|
};
|
||||||
|
|
||||||
function loadCalendars() {
|
function loadCalendars() {
|
||||||
ml.calendars()
|
$q.when(ml.calendars())
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
calendars = resp;
|
calendars = resp;
|
||||||
$scope.pager = pagerFactory.create(calendars.length, PAGE_SIZE, 1);
|
$scope.pager = pagerFactory.create(calendars.length, PAGE_SIZE, 1);
|
||||||
|
|
|
@ -18,6 +18,9 @@ import uiRoutes from 'ui/routes';
|
||||||
import { checkLicense } from 'plugins/ml/license/check_license';
|
import { checkLicense } from 'plugins/ml/license/check_license';
|
||||||
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
import { checkGetJobsPrivilege } from 'plugins/ml/privilege/check_privilege';
|
||||||
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { checkMlNodesAvailable } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { CalendarServiceProvider } from 'plugins/ml/services/calendar_service';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
import template from './create_calendar.html';
|
import template from './create_calendar.html';
|
||||||
|
|
||||||
|
@ -47,13 +50,14 @@ module.controller('MlCreateCalendar',
|
||||||
$scope,
|
$scope,
|
||||||
$route,
|
$route,
|
||||||
$location,
|
$location,
|
||||||
ml,
|
$q,
|
||||||
timefilter,
|
timefilter,
|
||||||
mlMessageBarService,
|
mlMessageBarService,
|
||||||
mlJobService,
|
Private) {
|
||||||
mlCalendarService) {
|
|
||||||
const msgs = mlMessageBarService;
|
const msgs = mlMessageBarService;
|
||||||
msgs.clear();
|
msgs.clear();
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlCalendarService = Private(CalendarServiceProvider);
|
||||||
|
|
||||||
const calendarId = $route.current.params.calendarId;
|
const calendarId = $route.current.params.calendarId;
|
||||||
$scope.isNewCalendar = (calendarId === undefined);
|
$scope.isNewCalendar = (calendarId === undefined);
|
||||||
|
@ -137,8 +141,8 @@ module.controller('MlCreateCalendar',
|
||||||
|
|
||||||
if (validateCalendarId(calendar.calendarId, $scope.validation.checks)) {
|
if (validateCalendarId(calendar.calendarId, $scope.validation.checks)) {
|
||||||
$scope.saveLock = true;
|
$scope.saveLock = true;
|
||||||
const saveFunc = $scope.isNewCalendar ? ml.addCalendar : ml.updateCalendar;
|
const saveFunc = $scope.isNewCalendar ? (c => ml.addCalendar(c)) : (c => ml.updateCalendar(c));
|
||||||
saveFunc(calendar)
|
$q.when(saveFunc(calendar))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
$location.path('settings/calendars_list');
|
$location.path('settings/calendars_list');
|
||||||
$scope.saveLock = false;
|
$scope.saveLock = false;
|
||||||
|
|
|
@ -32,6 +32,7 @@ import { isJobVersionGte } from '../../../common/util/job_utils';
|
||||||
import { parseInterval } from '../../../common/util/parse_interval';
|
import { parseInterval } from '../../../common/util/parse_interval';
|
||||||
import { Modal } from './modal';
|
import { Modal } from './modal';
|
||||||
import { PROGRESS_STATES } from './progress_states';
|
import { PROGRESS_STATES } from './progress_states';
|
||||||
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
|
|
||||||
const FORECAST_JOB_MIN_VERSION = '6.1.0'; // Forecasting only allowed for jobs created >= 6.1.0.
|
const FORECAST_JOB_MIN_VERSION = '6.1.0'; // Forecasting only allowed for jobs created >= 6.1.0.
|
||||||
const FORECASTS_VIEW_MAX = 5; // Display links to a maximum of 5 forecasts.
|
const FORECASTS_VIEW_MAX = 5; // Display links to a maximum of 5 forecasts.
|
||||||
|
@ -122,7 +123,7 @@ class ForecastingModal extends Component {
|
||||||
jobOpeningState: PROGRESS_STATES.WAITING
|
jobOpeningState: PROGRESS_STATES.WAITING
|
||||||
});
|
});
|
||||||
|
|
||||||
this.props.jobService.openJob(this.props.job.job_id)
|
this.props.mlJobService.openJob(this.props.job.job_id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
// If open was successful run the forecast, then close the job again.
|
// If open was successful run the forecast, then close the job again.
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -159,7 +160,7 @@ class ForecastingModal extends Component {
|
||||||
// formats accepted by Kibana (w, M, y) are not valid formats in Elasticsearch.
|
// formats accepted by Kibana (w, M, y) are not valid formats in Elasticsearch.
|
||||||
const durationInSeconds = parseInterval(this.state.newForecastDuration).asSeconds();
|
const durationInSeconds = parseInterval(this.state.newForecastDuration).asSeconds();
|
||||||
|
|
||||||
this.props.forecastService.runForecast(this.props.job.job_id, `${durationInSeconds}s`)
|
this.props.mlForecastService.runForecast(this.props.job.job_id, `${durationInSeconds}s`)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
// Endpoint will return { acknowledged:true, id: <now timestamp> } before forecast is complete.
|
// Endpoint will return { acknowledged:true, id: <now timestamp> } before forecast is complete.
|
||||||
// So wait for results and then refresh the dashboard to the end of the forecast.
|
// So wait for results and then refresh the dashboard to the end of the forecast.
|
||||||
|
@ -179,7 +180,7 @@ class ForecastingModal extends Component {
|
||||||
let previousProgress = 0;
|
let previousProgress = 0;
|
||||||
let noProgressMs = 0;
|
let noProgressMs = 0;
|
||||||
this.forecastChecker = setInterval(() => {
|
this.forecastChecker = setInterval(() => {
|
||||||
this.props.forecastService.getForecastRequestStats(this.props.job, forecastId)
|
this.props.mlForecastService.getForecastRequestStats(this.props.job, forecastId)
|
||||||
.then((resp) => {
|
.then((resp) => {
|
||||||
// Get the progress (stats value is between 0 and 1).
|
// Get the progress (stats value is between 0 and 1).
|
||||||
const progress = _.get(resp, ['stats', 'forecast_progress'], previousProgress);
|
const progress = _.get(resp, ['stats', 'forecast_progress'], previousProgress);
|
||||||
|
@ -197,7 +198,7 @@ class ForecastingModal extends Component {
|
||||||
|
|
||||||
if (closeJobAfterRunning === true) {
|
if (closeJobAfterRunning === true) {
|
||||||
this.setState({ jobClosingState: PROGRESS_STATES.WAITING });
|
this.setState({ jobClosingState: PROGRESS_STATES.WAITING });
|
||||||
this.props.jobService.closeJob(this.props.job.job_id)
|
this.props.mlJobService.closeJob(this.props.job.job_id)
|
||||||
.then(() => {
|
.then(() => {
|
||||||
this.setState({
|
this.setState({
|
||||||
jobClosingState: PROGRESS_STATES.DONE
|
jobClosingState: PROGRESS_STATES.DONE
|
||||||
|
@ -262,7 +263,7 @@ class ForecastingModal extends Component {
|
||||||
forecast_status: FORECAST_REQUEST_STATE.FINISHED
|
forecast_status: FORECAST_REQUEST_STATE.FINISHED
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this.props.forecastService.getForecastsSummary(
|
this.props.mlForecastService.getForecastsSummary(
|
||||||
job,
|
job,
|
||||||
statusFinishedQuery,
|
statusFinishedQuery,
|
||||||
bounds.min.valueOf(),
|
bounds.min.valueOf(),
|
||||||
|
@ -281,14 +282,15 @@ class ForecastingModal extends Component {
|
||||||
// of partitioning fields.
|
// of partitioning fields.
|
||||||
const entityFieldNames = this.props.entities.map(entity => entity.fieldName);
|
const entityFieldNames = this.props.entities.map(entity => entity.fieldName);
|
||||||
if (entityFieldNames.length > 0) {
|
if (entityFieldNames.length > 0) {
|
||||||
this.props.fieldsService.getCardinalityOfFields(
|
ml.getCardinalityOfFields({
|
||||||
job.datafeed_config.indices,
|
index: job.datafeed_config.indices,
|
||||||
job.datafeed_config.types,
|
types: job.datafeed_config.types,
|
||||||
entityFieldNames,
|
fieldNames: entityFieldNames,
|
||||||
job.datafeed_config.query,
|
query: job.datafeed_config.query,
|
||||||
job.data_description.time_field,
|
timeFieldName: job.data_description.time_field,
|
||||||
job.data_counts.earliest_record_timestamp,
|
earliestMs: job.data_counts.earliest_record_timestamp,
|
||||||
job.data_counts.latest_record_timestamp)
|
latestMs: job.data_counts.latest_record_timestamp
|
||||||
|
})
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
let numPartitions = 1;
|
let numPartitions = 1;
|
||||||
Object.values(results).forEach((cardinality) => {
|
Object.values(results).forEach((cardinality) => {
|
||||||
|
@ -397,9 +399,8 @@ ForecastingModal.propTypes = {
|
||||||
job: PropTypes.object,
|
job: PropTypes.object,
|
||||||
detectorIndex: PropTypes.number,
|
detectorIndex: PropTypes.number,
|
||||||
entities: PropTypes.array,
|
entities: PropTypes.array,
|
||||||
forecastService: PropTypes.object.isRequired,
|
mlForecastService: PropTypes.object.isRequired,
|
||||||
jobService: PropTypes.object.isRequired,
|
mlJobService: PropTypes.object.isRequired,
|
||||||
fieldsService: PropTypes.object.isRequired,
|
|
||||||
loadForForecastId: PropTypes.func,
|
loadForForecastId: PropTypes.func,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -10,24 +10,23 @@ import 'ngreact';
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml', ['react']);
|
const module = uiModules.get('apps/ml', ['react']);
|
||||||
|
|
||||||
import { FieldsServiceProvider } from 'plugins/ml/services/fields_service';
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { ForecastServiceProvider } from 'plugins/ml/services/forecast_service';
|
||||||
import { ForecastingModal } from './forecasting_modal';
|
import { ForecastingModal } from './forecasting_modal';
|
||||||
|
|
||||||
module.directive('mlForecastingModal', function ($injector, Private) {
|
module.directive('mlForecastingModal', function ($injector) {
|
||||||
const forecastService = $injector.get('mlForecastService');
|
const Private = $injector.get('Private');
|
||||||
const jobService = $injector.get('mlJobService');
|
const mlJobService = Private(JobServiceProvider);
|
||||||
const fieldsService = Private(FieldsServiceProvider);
|
const mlForecastService = Private(ForecastServiceProvider);
|
||||||
const timefilter = $injector.get('timefilter');
|
const timefilter = $injector.get('timefilter');
|
||||||
const reactDirective = $injector.get('reactDirective');
|
const reactDirective = $injector.get('reactDirective');
|
||||||
|
|
||||||
return reactDirective(
|
return reactDirective(
|
||||||
ForecastingModal,
|
ForecastingModal,
|
||||||
undefined,
|
undefined,
|
||||||
{ restrict: 'E' },
|
{ restrict: 'E' },
|
||||||
{
|
{
|
||||||
forecastService,
|
mlForecastService,
|
||||||
jobService,
|
mlJobService,
|
||||||
fieldsService,
|
|
||||||
timefilter
|
timefilter
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -31,6 +31,7 @@ import ContextChartMask from 'plugins/ml/timeseriesexplorer/context_chart_mask';
|
||||||
import { findNearestChartPointToTime } from 'plugins/ml/timeseriesexplorer/timeseriesexplorer_utils';
|
import { findNearestChartPointToTime } from 'plugins/ml/timeseriesexplorer/timeseriesexplorer_utils';
|
||||||
import 'plugins/ml/filters/format_value';
|
import 'plugins/ml/filters/format_value';
|
||||||
import { mlEscape } from 'plugins/ml/util/string_utils';
|
import { mlEscape } from 'plugins/ml/util/string_utils';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
@ -38,15 +39,15 @@ const module = uiModules.get('apps/ml');
|
||||||
module.directive('mlTimeseriesChart', function (
|
module.directive('mlTimeseriesChart', function (
|
||||||
$compile,
|
$compile,
|
||||||
$timeout,
|
$timeout,
|
||||||
Private,
|
|
||||||
timefilter,
|
timefilter,
|
||||||
mlAnomaliesTableService,
|
mlAnomaliesTableService,
|
||||||
mlFieldFormatService,
|
|
||||||
formatValueFilter,
|
formatValueFilter,
|
||||||
|
Private,
|
||||||
mlChartTooltipService) {
|
mlChartTooltipService) {
|
||||||
|
|
||||||
function link(scope, element) {
|
function link(scope, element) {
|
||||||
|
|
||||||
|
const mlFieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
// Key dimensions for the viz and constituent charts.
|
// Key dimensions for the viz and constituent charts.
|
||||||
let svgWidth = angular.element('.results-container').width();
|
let svgWidth = angular.element('.results-container').width();
|
||||||
const focusZoomPanelHeight = 25;
|
const focusZoomPanelHeight = 25;
|
||||||
|
|
|
@ -8,19 +8,19 @@
|
||||||
|
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
import { FieldsServiceProvider } from 'plugins/ml/services/fields_service';
|
import { ml } from 'plugins/ml/services/ml_api_service';
|
||||||
import { isModelPlotEnabled } from 'plugins/ml/../common/util/job_utils';
|
import { isModelPlotEnabled } from 'plugins/ml/../common/util/job_utils';
|
||||||
import { buildConfigFromDetector } from 'plugins/ml/util/chart_config_builder';
|
import { buildConfigFromDetector } from 'plugins/ml/util/chart_config_builder';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
|
|
||||||
import { uiModules } from 'ui/modules';
|
import { uiModules } from 'ui/modules';
|
||||||
const module = uiModules.get('apps/ml');
|
const module = uiModules.get('apps/ml');
|
||||||
|
|
||||||
module.service('mlTimeSeriesSearchService', function (
|
module.service('mlTimeSeriesSearchService', function (
|
||||||
$q,
|
$q,
|
||||||
$timeout,
|
Private) {
|
||||||
Private,
|
|
||||||
es,
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
mlResultsService) {
|
|
||||||
|
|
||||||
this.getMetricData = function (job, detectorIndex, entityFields, earliestMs, latestMs, interval) {
|
this.getMetricData = function (job, detectorIndex, entityFields, earliestMs, latestMs, interval) {
|
||||||
if (isModelPlotEnabled(job, detectorIndex, entityFields)) {
|
if (isModelPlotEnabled(job, detectorIndex, entityFields)) {
|
||||||
|
@ -63,7 +63,7 @@ module.service('mlTimeSeriesSearchService', function (
|
||||||
interval
|
interval
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = {
|
const obj = {
|
||||||
success: true,
|
success: true,
|
||||||
results: {}
|
results: {}
|
||||||
|
@ -90,13 +90,13 @@ module.service('mlTimeSeriesSearchService', function (
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -106,7 +106,7 @@ module.service('mlTimeSeriesSearchService', function (
|
||||||
// Queries Elasticsearch if necessary to obtain the distinct count of entities
|
// Queries Elasticsearch if necessary to obtain the distinct count of entities
|
||||||
// for which data is being plotted.
|
// for which data is being plotted.
|
||||||
this.getChartDetails = function (job, detectorIndex, entityFields, earliestMs, latestMs) {
|
this.getChartDetails = function (job, detectorIndex, entityFields, earliestMs, latestMs) {
|
||||||
const deferred = $q.defer();
|
return $q((resolve, reject) => {
|
||||||
const obj = { success: true, results: { functionLabel: '', entityData: { entities: [] } } };
|
const obj = { success: true, results: { functionLabel: '', entityData: { entities: [] } } };
|
||||||
|
|
||||||
const chartConfig = buildConfigFromDetector(job, detectorIndex);
|
const chartConfig = buildConfigFromDetector(job, detectorIndex);
|
||||||
|
@ -126,18 +126,18 @@ module.service('mlTimeSeriesSearchService', function (
|
||||||
if (blankEntityFields.length === 0) {
|
if (blankEntityFields.length === 0) {
|
||||||
obj.results.entityData.count = 1;
|
obj.results.entityData.count = 1;
|
||||||
obj.results.entityData.entities = entityFields;
|
obj.results.entityData.entities = entityFields;
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
} else {
|
} else {
|
||||||
const entityFieldNames = _.map(blankEntityFields, 'fieldName');
|
const entityFieldNames = _.map(blankEntityFields, 'fieldName');
|
||||||
const fieldsService = Private(FieldsServiceProvider);
|
ml.getCardinalityOfFields({
|
||||||
fieldsService.getCardinalityOfFields(
|
index: chartConfig.datafeedConfig.indices,
|
||||||
chartConfig.datafeedConfig.indices,
|
types: chartConfig.datafeedConfig.types,
|
||||||
chartConfig.datafeedConfig.types,
|
fieldNames: entityFieldNames,
|
||||||
entityFieldNames,
|
query: chartConfig.datafeedConfig.query,
|
||||||
chartConfig.datafeedConfig.query,
|
timeFieldName: chartConfig.timeField,
|
||||||
chartConfig.timeField,
|
|
||||||
earliestMs,
|
earliestMs,
|
||||||
latestMs)
|
latestMs
|
||||||
|
})
|
||||||
.then((results) => {
|
.then((results) => {
|
||||||
_.each(blankEntityFields, (field) => {
|
_.each(blankEntityFields, (field) => {
|
||||||
obj.results.entityData.entities.push({
|
obj.results.entityData.entities.push({
|
||||||
|
@ -146,14 +146,14 @@ module.service('mlTimeSeriesSearchService', function (
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
deferred.resolve(obj);
|
resolve(obj);
|
||||||
})
|
})
|
||||||
.catch((resp) => {
|
.catch((resp) => {
|
||||||
deferred.reject(resp);
|
reject(resp);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return deferred.promise;
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -16,10 +16,6 @@ import _ from 'lodash';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
|
||||||
import 'plugins/ml/components/anomalies_table';
|
import 'plugins/ml/components/anomalies_table';
|
||||||
import 'plugins/ml/services/field_format_service';
|
|
||||||
import 'plugins/ml/services/forecast_service';
|
|
||||||
import 'plugins/ml/services/job_service';
|
|
||||||
import 'plugins/ml/services/results_service';
|
|
||||||
|
|
||||||
import { notify } from 'ui/notify';
|
import { notify } from 'ui/notify';
|
||||||
import uiRoutes from 'ui/routes';
|
import uiRoutes from 'ui/routes';
|
||||||
|
@ -42,8 +38,12 @@ import {
|
||||||
processScheduledEventsForChart } from 'plugins/ml/timeseriesexplorer/timeseriesexplorer_utils';
|
processScheduledEventsForChart } from 'plugins/ml/timeseriesexplorer/timeseriesexplorer_utils';
|
||||||
import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher';
|
import { refreshIntervalWatcher } from 'plugins/ml/util/refresh_interval_watcher';
|
||||||
import { IntervalHelperProvider, getBoundsRoundedToInterval } from 'plugins/ml/util/ml_time_buckets';
|
import { IntervalHelperProvider, getBoundsRoundedToInterval } from 'plugins/ml/util/ml_time_buckets';
|
||||||
|
import { ResultsServiceProvider } from 'plugins/ml/services/results_service';
|
||||||
import template from './timeseriesexplorer.html';
|
import template from './timeseriesexplorer.html';
|
||||||
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||||
|
import { JobServiceProvider } from 'plugins/ml/services/job_service';
|
||||||
|
import { FieldFormatServiceProvider } from 'plugins/ml/services/field_format_service';
|
||||||
|
import { ForecastServiceProvider } from 'plugins/ml/services/forecast_service';
|
||||||
|
|
||||||
uiRoutes
|
uiRoutes
|
||||||
.when('/timeseriesexplorer/?', {
|
.when('/timeseriesexplorer/?', {
|
||||||
|
@ -63,20 +63,12 @@ module.controller('MlTimeSeriesExplorerController', function (
|
||||||
$scope,
|
$scope,
|
||||||
$route,
|
$route,
|
||||||
$timeout,
|
$timeout,
|
||||||
$compile,
|
|
||||||
$modal,
|
|
||||||
Private,
|
Private,
|
||||||
$q,
|
|
||||||
es,
|
|
||||||
timefilter,
|
timefilter,
|
||||||
AppState,
|
AppState,
|
||||||
mlJobService,
|
|
||||||
mlResultsService,
|
|
||||||
mlJobSelectService,
|
mlJobSelectService,
|
||||||
mlTimeSeriesSearchService,
|
mlTimeSeriesSearchService,
|
||||||
mlForecastService,
|
mlAnomaliesTableService) {
|
||||||
mlAnomaliesTableService,
|
|
||||||
mlFieldFormatService) {
|
|
||||||
|
|
||||||
$scope.timeFieldName = 'timestamp';
|
$scope.timeFieldName = 'timestamp';
|
||||||
timefilter.enableTimeRangeSelector();
|
timefilter.enableTimeRangeSelector();
|
||||||
|
@ -86,6 +78,10 @@ module.controller('MlTimeSeriesExplorerController', function (
|
||||||
const ANOMALIES_MAX_RESULTS = 500;
|
const ANOMALIES_MAX_RESULTS = 500;
|
||||||
const MAX_SCHEDULED_EVENTS = 10; // Max number of scheduled events displayed per bucket.
|
const MAX_SCHEDULED_EVENTS = 10; // Max number of scheduled events displayed per bucket.
|
||||||
const TimeBuckets = Private(IntervalHelperProvider);
|
const TimeBuckets = Private(IntervalHelperProvider);
|
||||||
|
const mlResultsService = Private(ResultsServiceProvider);
|
||||||
|
const mlJobService = Private(JobServiceProvider);
|
||||||
|
const mlFieldFormatService = Private(FieldFormatServiceProvider);
|
||||||
|
const mlForecastService = Private(ForecastServiceProvider);
|
||||||
|
|
||||||
$scope.jobPickerSelections = [];
|
$scope.jobPickerSelections = [];
|
||||||
$scope.selectedJob;
|
$scope.selectedJob;
|
||||||
|
|
44
x-pack/plugins/ml/public/util/clipboard_utils.js
Normal file
44
x-pack/plugins/ml/public/util/clipboard_utils.js
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
/*
|
||||||
|
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||||
|
* or more contributor license agreements. Licensed under the Elastic License;
|
||||||
|
* you may not use this file except in compliance with the Elastic License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// service for copying text to the users clipboard
|
||||||
|
// can only work when triggered via a user event, as part of an onclick or ng-click
|
||||||
|
// returns success
|
||||||
|
// e.g. mlClipboardService.copy("this could be abused!");
|
||||||
|
|
||||||
|
export function copyTextToClipboard(text) {
|
||||||
|
const textArea = document.createElement('textarea');
|
||||||
|
textArea.style.position = 'fixed';
|
||||||
|
textArea.style.top = 0;
|
||||||
|
textArea.style.left = 0;
|
||||||
|
textArea.style.width = '2em';
|
||||||
|
textArea.style.height = '2em';
|
||||||
|
textArea.style.padding = 0;
|
||||||
|
textArea.style.border = 'none';
|
||||||
|
textArea.style.outline = 'none';
|
||||||
|
textArea.style.boxShadow = 'none';
|
||||||
|
textArea.style.background = 'transparent';
|
||||||
|
textArea.value = text;
|
||||||
|
|
||||||
|
document.body.appendChild(textArea);
|
||||||
|
|
||||||
|
textArea.select();
|
||||||
|
|
||||||
|
let successful = false;
|
||||||
|
try {
|
||||||
|
successful = document.execCommand('copy');
|
||||||
|
const msg = successful ? 'successful' : 'unsuccessful';
|
||||||
|
console.log('Copying text command was ' + msg);
|
||||||
|
} catch (err) {
|
||||||
|
console.log('Oops, unable to copy');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.removeChild(textArea);
|
||||||
|
return successful;
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue