mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
[ML] Injectables refactor (#50512)
* [ML] Injectables refactor * removing unrelated files * additional typescript conversion * more typescript conversion * adding some return types * fixing eui errors * typescripting license checks * updated based on review * fixing merge conflict error * converting tests to jest * fixing types
This commit is contained in:
parent
2166044671
commit
eb4c47ef0c
63 changed files with 570 additions and 655 deletions
|
@ -1,70 +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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import { parseInterval } from '../parse_interval';
|
||||
import expect from '@kbn/expect';
|
||||
|
||||
describe('ML parse interval util', function () {
|
||||
it('correctly parses an interval containing unit and value', function () {
|
||||
let duration = parseInterval('1d');
|
||||
expect(duration.as('d')).to.be(1);
|
||||
|
||||
duration = parseInterval('2y');
|
||||
expect(duration.as('y')).to.be(2);
|
||||
|
||||
duration = parseInterval('5M');
|
||||
expect(duration.as('M')).to.be(5);
|
||||
|
||||
duration = parseInterval('5m');
|
||||
expect(duration.as('m')).to.be(5);
|
||||
|
||||
duration = parseInterval('250ms');
|
||||
expect(duration.as('ms')).to.be(250);
|
||||
|
||||
duration = parseInterval('100s');
|
||||
expect(duration.as('s')).to.be(100);
|
||||
|
||||
duration = parseInterval('23d');
|
||||
expect(duration.as('d')).to.be(23);
|
||||
|
||||
duration = parseInterval('52w');
|
||||
expect(duration.as('w')).to.be(52);
|
||||
|
||||
duration = parseInterval('0s');
|
||||
expect(duration.as('s')).to.be(0);
|
||||
|
||||
duration = parseInterval('0h');
|
||||
expect(duration.as('h')).to.be(0);
|
||||
|
||||
});
|
||||
|
||||
it('correctly handles zero value intervals', function () {
|
||||
let duration = parseInterval('0h');
|
||||
expect(duration.as('h')).to.be(0);
|
||||
|
||||
duration = parseInterval('0d');
|
||||
expect(duration).to.not.be.ok();
|
||||
});
|
||||
|
||||
it('returns null for an invalid interval', function () {
|
||||
let duration = parseInterval('');
|
||||
expect(duration).to.not.be.ok();
|
||||
|
||||
duration = parseInterval(null);
|
||||
expect(duration).to.not.be.ok();
|
||||
|
||||
duration = parseInterval('234asdf');
|
||||
expect(duration).to.not.be.ok();
|
||||
|
||||
duration = parseInterval('m');
|
||||
expect(duration).to.not.be.ok();
|
||||
|
||||
duration = parseInterval('1.5h');
|
||||
expect(duration).to.not.be.ok();
|
||||
});
|
||||
});
|
|
@ -1,6 +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.
|
||||
*/
|
||||
export function tabColor(name: string): string;
|
|
@ -4,9 +4,7 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import * as euiVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
import { stringHash } from './string_utils';
|
||||
|
||||
import euiVars from '@elastic/eui/dist/eui_theme_dark.json';
|
||||
|
||||
const COLORS = [
|
||||
euiVars.euiColorVis0,
|
||||
|
@ -20,18 +18,32 @@ const COLORS = [
|
|||
euiVars.euiColorVis8,
|
||||
euiVars.euiColorVis9,
|
||||
euiVars.euiColorDarkShade,
|
||||
euiVars.euiColorPrimary
|
||||
euiVars.euiColorPrimary,
|
||||
];
|
||||
|
||||
const colorMap = {};
|
||||
const colorMap: Record<string, string> = {};
|
||||
|
||||
export function tabColor(name) {
|
||||
export function tabColor(name: string): string {
|
||||
if (colorMap[name] === undefined) {
|
||||
const n = stringHash(name);
|
||||
const color = COLORS[(n % COLORS.length)];
|
||||
const color = COLORS[n % COLORS.length];
|
||||
colorMap[name] = color;
|
||||
return color;
|
||||
} else {
|
||||
return colorMap[name];
|
||||
}
|
||||
}
|
||||
|
||||
function stringHash(str: string): number {
|
||||
let hash = 0;
|
||||
let chr = 0;
|
||||
if (str.length === 0) {
|
||||
return hash;
|
||||
}
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
chr = str.charCodeAt(i);
|
||||
hash = (hash << 5) - hash + chr; // eslint-disable-line no-bitwise
|
||||
hash |= 0; // eslint-disable-line no-bitwise
|
||||
}
|
||||
return hash < 0 ? hash * -2 : hash;
|
||||
}
|
|
@ -33,3 +33,5 @@ export const ML_DATA_PREVIEW_COUNT: number;
|
|||
export function isJobIdValid(jobId: string): boolean;
|
||||
|
||||
export function processCreatedBy(customSettings: { created_by?: string }): void;
|
||||
|
||||
export function mlFunctionToESAggregation(functionName: string): string | null;
|
||||
|
|
34
x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts
Normal file
34
x-pack/legacy/plugins/ml/common/util/parse_interval.test.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* 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 { parseInterval } from './parse_interval';
|
||||
|
||||
describe('ML parse interval util', () => {
|
||||
test('correctly parses an interval containing unit and value', () => {
|
||||
expect(parseInterval('1d')!.as('d')).toBe(1);
|
||||
expect(parseInterval('2y')!.as('y')).toBe(2);
|
||||
expect(parseInterval('5M')!.as('M')).toBe(5);
|
||||
expect(parseInterval('5m')!.as('m')).toBe(5);
|
||||
expect(parseInterval('250ms')!.as('ms')).toBe(250);
|
||||
expect(parseInterval('100s')!.as('s')).toBe(100);
|
||||
expect(parseInterval('23d')!.as('d')).toBe(23);
|
||||
expect(parseInterval('52w')!.as('w')).toBe(52);
|
||||
expect(parseInterval('0s')!.as('s')).toBe(0);
|
||||
expect(parseInterval('0s')!.as('h')).toBe(0);
|
||||
});
|
||||
|
||||
test('correctly handles zero value intervals', () => {
|
||||
expect(parseInterval('0h')!.as('h')).toBe(0);
|
||||
expect(parseInterval('0d')).toBe(null);
|
||||
});
|
||||
|
||||
test('returns null for an invalid interval', () => {
|
||||
expect(parseInterval('')).toBe(null);
|
||||
expect(parseInterval('234asdf')).toBe(null);
|
||||
expect(parseInterval('m')).toBe(null);
|
||||
expect(parseInterval('1.5h')).toBe(null);
|
||||
});
|
||||
});
|
|
@ -4,17 +4,17 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import moment from 'moment';
|
||||
import { duration, Duration, unitOfTime } from 'moment';
|
||||
import dateMath from '@elastic/datemath';
|
||||
|
||||
type SupportedUnits = unitOfTime.Base;
|
||||
|
||||
// Assume interval is in the form (value)(unit), such as "1h"
|
||||
const INTERVAL_STRING_RE = new RegExp('^([0-9]*)\\s*(' + dateMath.units.join('|') + ')$');
|
||||
|
||||
// moment.js is only designed to allow fractional values between 0 and 1
|
||||
// for units of hour or less.
|
||||
const SUPPORT_ZERO_DURATION_UNITS = ['ms', 's', 'm', 'h'];
|
||||
const SUPPORT_ZERO_DURATION_UNITS: SupportedUnits[] = ['ms', 's', 'm', 'h'];
|
||||
|
||||
// Parses an interval String, such as 7d, 1h or 30m to a moment duration.
|
||||
// Differs from the Kibana ui/utils/parse_interval in the following ways:
|
||||
|
@ -25,22 +25,25 @@ const SUPPORT_ZERO_DURATION_UNITS = ['ms', 's', 'm', 'h'];
|
|||
// to work with units less than 'day'.
|
||||
// 3. Fractional intervals e.g. 1.5h or 4.5d are not allowed, in line with the behaviour
|
||||
// of the Elasticsearch date histogram aggregation.
|
||||
export function parseInterval(interval) {
|
||||
const matches = String(interval).trim().match(INTERVAL_STRING_RE);
|
||||
if (!Array.isArray(matches)) return null;
|
||||
if (matches.length < 3) return null;
|
||||
export function parseInterval(interval: string): Duration | null {
|
||||
const matches = String(interval)
|
||||
.trim()
|
||||
.match(INTERVAL_STRING_RE);
|
||||
if (!Array.isArray(matches) || matches.length < 3) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const value = parseInt(matches[1]);
|
||||
const unit = matches[2];
|
||||
const value = parseInt(matches[1], 10);
|
||||
const unit = matches[2] as SupportedUnits;
|
||||
|
||||
// In line with moment.js, only allow zero value intervals when the unit is less than 'day'.
|
||||
// And check for isNaN as e.g. valueless 'm' will pass the regex test.
|
||||
if (isNaN(value) || (value < 1 && SUPPORT_ZERO_DURATION_UNITS.indexOf(unit) === -1)) {
|
||||
if (isNaN(value) || (value < 1 && SUPPORT_ZERO_DURATION_UNITS.indexOf(unit) === -1)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return moment.duration(value, unit);
|
||||
return duration(value, unit);
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
|
@ -1,8 +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.
|
||||
*/
|
||||
|
||||
export function renderTemplate(str: string, data: string): string;
|
||||
export function stringHash(str: string): string;
|
|
@ -4,29 +4,24 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import { renderTemplate } from '../string_utils';
|
||||
import { renderTemplate } from './string_utils';
|
||||
|
||||
describe('ML - string utils', () => {
|
||||
describe('renderTemplate', () => {
|
||||
|
||||
it('returns plain string', () => {
|
||||
test('returns plain string', () => {
|
||||
const templateString = 'plain string';
|
||||
const result = renderTemplate(templateString);
|
||||
expect(result).to.be(result);
|
||||
expect(result).toBe(result);
|
||||
});
|
||||
it('returns rendered template with one replacement', () => {
|
||||
test('returns rendered template with one replacement', () => {
|
||||
const templateString = 'string with {{one}} replacement';
|
||||
const result = renderTemplate(templateString, { one: '1' });
|
||||
expect(result).to.be('string with 1 replacement');
|
||||
expect(result).toBe('string with 1 replacement');
|
||||
});
|
||||
it('returns rendered template with two replacements', () => {
|
||||
test('returns rendered template with two replacements', () => {
|
||||
const templateString = 'string with {{one}} replacement, and a {{two}} one.';
|
||||
const result = renderTemplate(templateString, { one: '1', two: '2nd' });
|
||||
expect(result).to.be('string with 1 replacement, and a 2nd one.');
|
||||
expect(result).toBe('string with 1 replacement, and a 2nd one.');
|
||||
});
|
||||
|
||||
});
|
||||
});
|
|
@ -4,15 +4,12 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
// A simple template renderer, it replaces mustache/angular style {{...}} tags with
|
||||
// the values provided via the data object
|
||||
export function renderTemplate(str, data) {
|
||||
export function renderTemplate(str: string, data?: Record<string, string>): string {
|
||||
const matches = str.match(/{{(.*?)}}/g);
|
||||
|
||||
if (Array.isArray(matches)) {
|
||||
if (Array.isArray(matches) && data !== undefined) {
|
||||
matches.forEach(v => {
|
||||
str = str.replace(v, data[v.replace(/{{|}}/g, '')]);
|
||||
});
|
||||
|
@ -20,17 +17,3 @@ export function renderTemplate(str, data) {
|
|||
|
||||
return str;
|
||||
}
|
||||
|
||||
export function stringHash(str) {
|
||||
let hash = 0;
|
||||
let chr = '';
|
||||
if (str.length === 0) {
|
||||
return hash;
|
||||
}
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
chr = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0;
|
||||
}
|
||||
return hash < 0 ? hash * -2 : hash;
|
||||
}
|
|
@ -1,7 +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.
|
||||
*/
|
||||
|
||||
export function isValidJson(json: string): boolean;
|
|
@ -1,54 +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.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import { VALIDATION_STATUS } from '../constants/validation';
|
||||
|
||||
// get the most severe status level from a list of messages
|
||||
const contains = (arr, str) => arr.findIndex(v => v === str) >= 0;
|
||||
export function getMostSevereMessageStatus(messages) {
|
||||
const statuses = messages.map(m => m.status);
|
||||
return [
|
||||
VALIDATION_STATUS.INFO,
|
||||
VALIDATION_STATUS.WARNING,
|
||||
VALIDATION_STATUS.ERROR
|
||||
].reduce((previous, current) => {
|
||||
return contains(statuses, current) ? current : previous;
|
||||
}, VALIDATION_STATUS.SUCCESS);
|
||||
}
|
||||
|
||||
// extends an angular directive's scope with the necessary methods
|
||||
// needed to embed the job validation button
|
||||
export function addJobValidationMethods($scope, service) {
|
||||
$scope.getDuration = () => ({
|
||||
start: $scope.formConfig.start,
|
||||
end: $scope.formConfig.end
|
||||
});
|
||||
|
||||
// isCurrentJobConfig is used to track if the form configuration
|
||||
// changed since the last job validation was done
|
||||
$scope.isCurrentJobConfig = false;
|
||||
// need to pass true as third argument here to track granular changes
|
||||
$scope.$watch('formConfig', () => { $scope.isCurrentJobConfig = false; }, true);
|
||||
$scope.getJobConfig = () => {
|
||||
$scope.isCurrentJobConfig = true;
|
||||
return service.getJobFromConfig($scope.formConfig);
|
||||
};
|
||||
}
|
||||
|
||||
export function isValidJson(json) {
|
||||
if(json === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
JSON.parse(json);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
33
x-pack/legacy/plugins/ml/common/util/validation_utils.ts
Normal file
33
x-pack/legacy/plugins/ml/common/util/validation_utils.ts
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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 { VALIDATION_STATUS } from '../constants/validation';
|
||||
|
||||
// get the most severe status level from a list of messages
|
||||
const contains = (arr: string[], str: string) => arr.indexOf(str) >= 0;
|
||||
|
||||
export function getMostSevereMessageStatus(messages: Array<{ status: string }>): VALIDATION_STATUS {
|
||||
const statuses = messages.map(m => m.status);
|
||||
return [VALIDATION_STATUS.INFO, VALIDATION_STATUS.WARNING, VALIDATION_STATUS.ERROR].reduce(
|
||||
(previous, current) => {
|
||||
return contains(statuses, current) ? current : previous;
|
||||
},
|
||||
VALIDATION_STATUS.SUCCESS
|
||||
);
|
||||
}
|
||||
|
||||
export function isValidJson(json: string) {
|
||||
if (json === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
JSON.parse(json);
|
||||
return true;
|
||||
} catch (error) {
|
||||
return false;
|
||||
}
|
||||
}
|
0
x-pack/legacy/plugins/ml/index.ts
Normal file → Executable file
0
x-pack/legacy/plugins/ml/index.ts
Normal file → Executable file
|
@ -33,7 +33,7 @@ import { ml } from '../../services/ml_api_service';
|
|||
import { mlJobService } from '../../services/job_service';
|
||||
import { getUrlForRecord, openCustomUrlWindow } from '../../util/custom_url_utils';
|
||||
import { formatHumanReadableDateTimeSeconds } from '../../util/date_utils';
|
||||
import { getIndexPatterns } from '../../util/index_utils';
|
||||
import { getIndexPatternIdFromName } from '../../util/index_utils';
|
||||
import { replaceStringTokens } from '../../util/string_utils';
|
||||
|
||||
|
||||
|
@ -214,7 +214,6 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
|
|||
const { intl } = this.props;
|
||||
const categoryId = this.props.anomaly.entityValue;
|
||||
const record = this.props.anomaly.source;
|
||||
const indexPatterns = getIndexPatterns();
|
||||
|
||||
const job = mlJobService.getJob(this.props.anomaly.jobId);
|
||||
if (job === undefined) {
|
||||
|
@ -260,13 +259,7 @@ export const LinksMenu = injectI18n(class LinksMenu extends Component {
|
|||
// index configured in the datafeed. If a Kibana index pattern has not been created
|
||||
// for this index, then the user will see a warning message on the Discover tab advising
|
||||
// them that no matching index pattern has been configured.
|
||||
let indexPatternId = index;
|
||||
for (let j = 0; j < indexPatterns.length; j++) {
|
||||
if (indexPatterns[j].get('title') === index) {
|
||||
indexPatternId = indexPatterns[j].id;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const indexPatternId = getIndexPatternIdFromName(index) || index;
|
||||
|
||||
// Get the definition of the category and use the terms or regex to view the
|
||||
// matching events in the Kibana Discover tab depending on whether the
|
||||
|
|
|
@ -17,6 +17,5 @@ export const kibanaContextValueMock = {
|
|||
currentIndexPattern: indexPatternMock,
|
||||
currentSavedSearch: savedSearchMock,
|
||||
indexPatterns: indexPatternsMock,
|
||||
kbnBaseUrl: 'url',
|
||||
kibanaConfig: kibanaConfigMock,
|
||||
};
|
||||
|
|
|
@ -21,7 +21,6 @@ export interface KibanaContextValue {
|
|||
currentIndexPattern: IndexPattern;
|
||||
currentSavedSearch: SavedSearch;
|
||||
indexPatterns: IndexPatterns;
|
||||
kbnBaseUrl: string;
|
||||
kibanaConfig: KibanaConfigTypeFix;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@ export const useKibanaContext = () => {
|
|||
context.currentIndexPattern === undefined ||
|
||||
context.currentSavedSearch === undefined ||
|
||||
context.indexPatterns === undefined ||
|
||||
context.kbnBaseUrl === undefined ||
|
||||
context.kibanaConfig === undefined
|
||||
) {
|
||||
throw new Error('required attribute is undefined');
|
||||
|
|
|
@ -13,10 +13,9 @@ const module = uiModules.get('apps/ml', ['react']);
|
|||
|
||||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IPrivate } from 'ui/private';
|
||||
|
||||
import { InjectorService } from '../../../../common/types/angular';
|
||||
import { SearchItemsProvider } from '../../../jobs/new_job_new/utils/new_job_utils';
|
||||
import { createSearchItems } from '../../../jobs/new_job_new/utils/new_job_utils';
|
||||
|
||||
import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana';
|
||||
|
||||
|
@ -31,19 +30,20 @@ module.directive('mlDataFrameAnalyticsExploration', ($injector: InjectorService)
|
|||
globalState.fetch();
|
||||
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -13,10 +13,8 @@ const module = uiModules.get('apps/ml', ['react']);
|
|||
|
||||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IPrivate } from 'ui/private';
|
||||
|
||||
import { InjectorService } from '../../../../common/types/angular';
|
||||
import { SearchItemsProvider } from '../../../jobs/new_job_new/utils/new_job_utils';
|
||||
import { createSearchItems } from '../../../jobs/new_job_new/utils/new_job_utils';
|
||||
|
||||
import { KibanaConfigTypeFix, KibanaContext } from '../../../contexts/kibana';
|
||||
|
||||
|
@ -28,19 +26,20 @@ module.directive('mlDataFrameAnalyticsManagement', ($injector: InjectorService)
|
|||
restrict: 'E',
|
||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -4,38 +4,52 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import 'ngreact';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
|
||||
import { wrapInI18nContext } from 'ui/i18n';
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { getDataVisualizerBreadcrumbs } from './breadcrumbs';
|
||||
import { checkBasicLicense } from '../license/check_license';
|
||||
import { checkFindFileStructurePrivilege } from '../privilege/check_privilege';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
<datavisualizer-selector data-test-subj="mlPageDataVisualizerSelector" />
|
||||
`;
|
||||
|
||||
uiRoutes
|
||||
.when('/datavisualizer', {
|
||||
template,
|
||||
k7Breadcrumbs: getDataVisualizerBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkBasicLicense,
|
||||
privileges: checkFindFileStructurePrivilege,
|
||||
}
|
||||
});
|
||||
|
||||
uiRoutes.when('/datavisualizer', {
|
||||
template,
|
||||
k7Breadcrumbs: getDataVisualizerBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkBasicLicense,
|
||||
privileges: checkFindFileStructurePrivilege,
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
import { DatavisualizerSelector } from './datavisualizer_selector';
|
||||
|
||||
module.directive('datavisualizerSelector', function ($injector) {
|
||||
const reactDirective = $injector.get('reactDirective');
|
||||
module.directive('datavisualizerSelector', function() {
|
||||
return {
|
||||
scope: {},
|
||||
restrict: 'E',
|
||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
||||
ReactDOM.render(
|
||||
<I18nContext>
|
||||
<DatavisualizerSelector />
|
||||
</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
|
||||
return reactDirective(wrapInI18nContext(DatavisualizerSelector), undefined, { restrict: 'E' }, { });
|
||||
element.on('$destroy', () => {
|
||||
ReactDOM.unmountComponentAtNode(element[0]);
|
||||
scope.$destroy();
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
import { FormattedMessage } from '@kbn/i18n/react';
|
||||
import React, {
|
||||
Component,
|
||||
Component
|
||||
} from 'react';
|
||||
|
||||
import {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { ImportErrors } from '../import_errors';
|
|||
import { ImportSummary } from '../import_summary';
|
||||
import { ImportSettings } from '../import_settings';
|
||||
import { ExperimentalBadge } from '../experimental_badge';
|
||||
import { getIndexPatternNames, refreshIndexPatterns } from '../../../../util/index_utils';
|
||||
import { getIndexPatternNames, loadIndexPatterns } from '../../../../util/index_utils';
|
||||
import { ml } from '../../../../services/ml_api_service';
|
||||
import { hasImportPermission } from '../utils';
|
||||
|
||||
|
@ -359,7 +359,7 @@ export class ImportView extends Component {
|
|||
}
|
||||
|
||||
async loadIndexPatternNames() {
|
||||
await refreshIndexPatterns();
|
||||
await loadIndexPatterns();
|
||||
const indexPatternNames = getIndexPatternNames();
|
||||
this.setState({ indexPatternNames });
|
||||
}
|
||||
|
|
|
@ -1,49 +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.
|
||||
*/
|
||||
|
||||
|
||||
import 'ngreact';
|
||||
|
||||
import { wrapInI18nContext } from 'ui/i18n';
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { getFileDataVisualizerBreadcrumbs } from './breadcrumbs';
|
||||
import { checkBasicLicense } from '../../license/check_license';
|
||||
import { checkFindFileStructurePrivilege } from '../../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes';
|
||||
import { loadMlServerInfo } from '../../services/ml_server_info';
|
||||
import { loadIndexPatterns } from '../../util/index_utils';
|
||||
import { FileDataVisualizerPage } from './file_datavisualizer';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
<file-datavisualizer-page />
|
||||
`;
|
||||
|
||||
uiRoutes
|
||||
.when('/filedatavisualizer/?', {
|
||||
template,
|
||||
k7Breadcrumbs: getFileDataVisualizerBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkBasicLicense,
|
||||
privileges: checkFindFileStructurePrivilege,
|
||||
indexPatterns: loadIndexPatterns,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
loadMlServerInfo,
|
||||
}
|
||||
});
|
||||
|
||||
module.directive('fileDatavisualizerPage', function ($injector) {
|
||||
const reactDirective = $injector.get('reactDirective');
|
||||
const indexPatterns = $injector.get('indexPatterns');
|
||||
const kibanaConfig = $injector.get('config');
|
||||
|
||||
return reactDirective(wrapInI18nContext(FileDataVisualizerPage), undefined, { restrict: 'E' }, { indexPatterns, kibanaConfig });
|
||||
});
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
import { KibanaConfigTypeFix } from '../../contexts/kibana';
|
||||
// @ts-ignore
|
||||
import { getFileDataVisualizerBreadcrumbs } from './breadcrumbs';
|
||||
import { InjectorService } from '../../../common/types/angular';
|
||||
import { checkBasicLicense } from '../../license/check_license';
|
||||
import { checkFindFileStructurePrivilege } from '../../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../../ml_nodes_check/check_ml_nodes';
|
||||
import { loadMlServerInfo } from '../../services/ml_server_info';
|
||||
import { loadIndexPatterns } from '../../util/index_utils';
|
||||
// @ts-ignore
|
||||
import { FileDataVisualizerPage } from './file_datavisualizer';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
<file-datavisualizer-page />
|
||||
`;
|
||||
|
||||
uiRoutes.when('/filedatavisualizer/?', {
|
||||
template,
|
||||
k7Breadcrumbs: getFileDataVisualizerBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkBasicLicense,
|
||||
privileges: checkFindFileStructurePrivilege,
|
||||
indexPatterns: loadIndexPatterns,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
loadMlServerInfo,
|
||||
},
|
||||
});
|
||||
|
||||
interface Props {
|
||||
indexPatterns: IndexPatterns;
|
||||
kibanaConfig: KibanaConfigTypeFix;
|
||||
}
|
||||
|
||||
module.directive('fileDatavisualizerPage', function($injector: InjectorService) {
|
||||
return {
|
||||
scope: {},
|
||||
restrict: 'E',
|
||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
|
||||
const props: Props = {
|
||||
indexPatterns,
|
||||
kibanaConfig,
|
||||
};
|
||||
ReactDOM.render(
|
||||
<I18nContext>{React.createElement(FileDataVisualizerPage, props)}</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
|
||||
element.on('$destroy', () => {
|
||||
ReactDOM.unmountComponentAtNode(element[0]);
|
||||
scope.$destroy();
|
||||
});
|
||||
},
|
||||
};
|
||||
});
|
|
@ -4,5 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import './file_datavisualizer_directive';
|
|
@ -13,11 +13,10 @@ const module = uiModules.get('apps/ml', ['react']);
|
|||
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
import { IPrivate } from 'ui/private';
|
||||
import { InjectorService } from '../../../common/types/angular';
|
||||
|
||||
import { KibanaConfigTypeFix, KibanaContext } from '../../contexts/kibana/kibana_context';
|
||||
import { SearchItemsProvider } from '../../jobs/new_job_new/utils/new_job_utils';
|
||||
import { createSearchItems } from '../../jobs/new_job_new/utils/new_job_utils';
|
||||
|
||||
import { Page } from './page';
|
||||
|
||||
|
@ -27,19 +26,20 @@ module.directive('mlDataVisualizer', ($injector: InjectorService) => {
|
|||
restrict: 'E',
|
||||
link: (scope: ng.IScope, element: ng.IAugmentedJQuery) => {
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -23,7 +23,7 @@ import {
|
|||
import { getAnomalyExplorerBreadcrumbs } from './breadcrumbs';
|
||||
import { checkFullLicense } from '../license/check_license';
|
||||
import { checkGetJobsPrivilege } from '../privilege/check_privilege';
|
||||
import { getIndexPatterns, loadIndexPatterns } from '../util/index_utils';
|
||||
import { loadIndexPatterns } from '../util/index_utils';
|
||||
import { TimeBuckets } from 'plugins/ml/util/time_buckets';
|
||||
import { explorer$ } from './explorer_dashboard_service';
|
||||
import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service';
|
||||
|
@ -114,7 +114,7 @@ module.controller('MlExplorerController', function (
|
|||
}
|
||||
|
||||
// Populate the map of jobs / detectors / field formatters for the selected IDs.
|
||||
mlFieldFormatService.populateFormats(selectedJobIds, getIndexPatterns())
|
||||
mlFieldFormatService.populateFormats(selectedJobIds)
|
||||
.catch((err) => {
|
||||
console.log('Error populating field formats:', err);
|
||||
})
|
||||
|
|
|
@ -30,7 +30,6 @@ export const DedicatedIndexSwitch: FC = () => {
|
|||
checked={useDedicatedIndex}
|
||||
onChange={toggleModelPlot}
|
||||
data-test-subj="mlJobWizardSwitchUseDedicatedIndex"
|
||||
showLabel={false}
|
||||
label={i18n.translate(
|
||||
'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.useDedicatedIndex.title',
|
||||
{
|
||||
|
|
|
@ -30,7 +30,6 @@ export const ModelPlotSwitch: FC = () => {
|
|||
checked={modelPlotEnabled}
|
||||
onChange={toggleModelPlot}
|
||||
data-test-subj="mlJobWizardSwitchModelPlot"
|
||||
showLabel={false}
|
||||
label={i18n.translate(
|
||||
'xpack.ml.newJob.wizard.jobDetailsStep.advancedSection.enableModelPlot.title',
|
||||
{
|
||||
|
|
|
@ -44,7 +44,6 @@ export const SparseDataSwitch: FC = () => {
|
|||
checked={sparseData}
|
||||
onChange={toggleSparseData}
|
||||
data-test-subj="mlJobWizardSwitchSparseData"
|
||||
showLabel={false}
|
||||
label={i18n.translate('xpack.ml.newJob.wizard.pickFieldsStep.sparseData.title', {
|
||||
defaultMessage: 'Sparse data',
|
||||
})}
|
||||
|
|
|
@ -14,10 +14,8 @@ import { timefilter } from 'ui/timefilter';
|
|||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IPrivate } from 'ui/private';
|
||||
import { InjectorService } from '../../../../../common/types/angular';
|
||||
|
||||
import { SearchItemsProvider } from '../../../new_job_new/utils/new_job_utils';
|
||||
import { createSearchItems } from '../../../new_job_new/utils/new_job_utils';
|
||||
import { Page } from './page';
|
||||
|
||||
import { KibanaContext, KibanaConfigTypeFix } from '../../../../contexts/kibana';
|
||||
|
@ -32,18 +30,19 @@ module.directive('mlJobTypePage', ($injector: InjectorService) => {
|
|||
timefilter.disableAutoRefreshSelector();
|
||||
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -14,10 +14,8 @@ import { timefilter } from 'ui/timefilter';
|
|||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IPrivate } from 'ui/private';
|
||||
import { InjectorService } from '../../../../../common/types/angular';
|
||||
|
||||
import { SearchItemsProvider } from '../../utils/new_job_utils';
|
||||
import { createSearchItems } from '../../utils/new_job_utils';
|
||||
import { Page, PageProps } from './page';
|
||||
import { JOB_TYPE } from '../../common/job_creator/util/constants';
|
||||
|
||||
|
@ -32,9 +30,7 @@ module.directive('mlNewJobPage', ($injector: InjectorService) => {
|
|||
timefilter.disableAutoRefreshSelector();
|
||||
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
const existingJobsAndGroups = $route.current.locals.existingJobsAndGroups;
|
||||
|
||||
|
@ -43,15 +39,17 @@ module.directive('mlNewJobPage', ($injector: InjectorService) => {
|
|||
}
|
||||
const jobType: JOB_TYPE = $route.current.locals.jobType;
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -247,7 +247,6 @@ export const JobSettingsForm: FC<JobSettingsFormProps> = ({
|
|||
useDedicatedIndex: checked,
|
||||
});
|
||||
}}
|
||||
showLabel={false}
|
||||
label={i18n.translate('xpack.ml.newJob.recognize.useDedicatedIndexLabel', {
|
||||
defaultMessage: 'Use dedicated index',
|
||||
})}
|
||||
|
|
|
@ -14,10 +14,9 @@ import { timefilter } from 'ui/timefilter';
|
|||
import { IndexPatterns } from 'ui/index_patterns';
|
||||
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { IPrivate } from 'ui/private';
|
||||
import { InjectorService } from '../../../../common/types/angular';
|
||||
|
||||
import { SearchItemsProvider } from '../../new_job_new/utils/new_job_utils';
|
||||
import { createSearchItems } from '../../new_job_new/utils/new_job_utils';
|
||||
import { Page } from './page';
|
||||
|
||||
import { KibanaContext, KibanaConfigTypeFix } from '../../../contexts/kibana';
|
||||
|
@ -32,22 +31,22 @@ module.directive('mlRecognizePage', ($injector: InjectorService) => {
|
|||
timefilter.disableAutoRefreshSelector();
|
||||
|
||||
const indexPatterns = $injector.get<IndexPatterns>('indexPatterns');
|
||||
const kbnBaseUrl = $injector.get<string>('kbnBaseUrl');
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const Private = $injector.get<IPrivate>('Private');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
const moduleId = $route.current.params.id;
|
||||
const existingGroupIds: string[] = $route.current.locals.existingJobsAndGroups.groupIds;
|
||||
|
||||
const createSearchItems = Private(SearchItemsProvider);
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems();
|
||||
const { indexPattern, savedSearch, combinedQuery } = createSearchItems(
|
||||
kibanaConfig,
|
||||
$route.current.locals.indexPattern,
|
||||
$route.current.locals.savedSearch
|
||||
);
|
||||
const kibanaContext = {
|
||||
combinedQuery,
|
||||
currentIndexPattern: indexPattern,
|
||||
currentSavedSearch: savedSearch,
|
||||
indexPatterns,
|
||||
kbnBaseUrl,
|
||||
kibanaConfig,
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
import chrome from 'ui/chrome';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { toastNotifications } from 'ui/notify';
|
||||
import { IPrivate } from 'ui/private';
|
||||
import { mlJobService } from '../../../services/job_service';
|
||||
import { ml } from '../../../services/ml_api_service';
|
||||
import { KibanaObjects } from './page';
|
||||
|
@ -17,12 +16,7 @@ import { KibanaObjects } from './page';
|
|||
* Redirects to the Anomaly Explorer to view the jobs if they have been created,
|
||||
* or the recognizer job wizard for the module if not.
|
||||
*/
|
||||
export function checkViewOrCreateJobs(
|
||||
Private: IPrivate,
|
||||
$route: any,
|
||||
kbnBaseUrl: string,
|
||||
kbnUrl: any
|
||||
) {
|
||||
export function checkViewOrCreateJobs($route: any) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const moduleId = $route.current.params.id;
|
||||
const indexPatternId = $route.current.params.index;
|
||||
|
@ -58,7 +52,7 @@ export function checkViewOrCreateJobs(
|
|||
}),
|
||||
});
|
||||
|
||||
kbnUrl.redirect(`/jobs`);
|
||||
window.location.href = '#/jobs';
|
||||
reject();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { IndexPattern } from 'ui/index_patterns';
|
||||
import { SavedSearch } from 'src/legacy/core_plugins/kibana/public/discover/types';
|
||||
import { KibanaConfigTypeFix } from '../../../contexts/kibana';
|
||||
import { InjectorService } from '../../../../common/types/angular';
|
||||
import { esQuery, IIndexPattern } from '../../../../../../../../src/plugins/data/public';
|
||||
|
||||
export interface SearchItems {
|
||||
|
@ -18,55 +18,50 @@ export interface SearchItems {
|
|||
|
||||
// Provider for creating the items used for searching and job creation.
|
||||
// Uses the $route object to retrieve the indexPattern and savedSearch from the url
|
||||
export function SearchItemsProvider($injector: InjectorService) {
|
||||
const kibanaConfig = $injector.get<KibanaConfigTypeFix>('config');
|
||||
const $route = $injector.get<any>('$route');
|
||||
|
||||
function createSearchItems() {
|
||||
let indexPattern = $route.current.locals.indexPattern;
|
||||
export function createSearchItems(
|
||||
kibanaConfig: KibanaConfigTypeFix,
|
||||
indexPattern: IndexPattern,
|
||||
savedSearch: SavedSearch
|
||||
) {
|
||||
// query is only used by the data visualizer as it needs
|
||||
// a lucene query_string.
|
||||
// Using a blank query will cause match_all:{} to be used
|
||||
// when passed through luceneStringToDsl
|
||||
let query = {
|
||||
query: '',
|
||||
language: 'lucene',
|
||||
};
|
||||
|
||||
// query is only used by the data visualizer as it needs
|
||||
// a lucene query_string.
|
||||
// Using a blank query will cause match_all:{} to be used
|
||||
// when passed through luceneStringToDsl
|
||||
let query = {
|
||||
query: '',
|
||||
language: 'lucene',
|
||||
};
|
||||
let combinedQuery: any = {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
match_all: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
let combinedQuery: any = {
|
||||
bool: {
|
||||
must: [
|
||||
{
|
||||
match_all: {},
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
if (indexPattern.id === undefined && savedSearch.id !== undefined) {
|
||||
const searchSource = savedSearch.searchSource;
|
||||
indexPattern = searchSource.getField('index');
|
||||
|
||||
const savedSearch = $route.current.locals.savedSearch;
|
||||
if (indexPattern.id === undefined && savedSearch.id !== undefined) {
|
||||
const searchSource = savedSearch.searchSource;
|
||||
indexPattern = searchSource.getField('index');
|
||||
query = searchSource.getField('query');
|
||||
const fs = searchSource.getField('filter');
|
||||
|
||||
query = searchSource.getField('query');
|
||||
const fs = searchSource.getField('filter');
|
||||
const filters = fs.length ? fs : [];
|
||||
|
||||
const filters = fs.length ? fs : [];
|
||||
|
||||
const esQueryConfigs = esQuery.getEsQueryConfig(kibanaConfig);
|
||||
combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs);
|
||||
}
|
||||
|
||||
return {
|
||||
indexPattern,
|
||||
savedSearch,
|
||||
query,
|
||||
combinedQuery,
|
||||
};
|
||||
const esQueryConfigs = esQuery.getEsQueryConfig(kibanaConfig);
|
||||
combinedQuery = esQuery.buildEsQuery(indexPattern, [query], filters, esQueryConfigs);
|
||||
}
|
||||
|
||||
return createSearchItems;
|
||||
return {
|
||||
indexPattern,
|
||||
savedSearch,
|
||||
query,
|
||||
combinedQuery,
|
||||
};
|
||||
}
|
||||
|
||||
// Only model plot cardinality relevant
|
||||
|
|
|
@ -4,51 +4,44 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import React from 'react';
|
||||
// @ts-ignore No declaration file for module
|
||||
import { banners } from 'ui/notify';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
// @ts-ignore No declaration file for module
|
||||
import { xpackInfo } from '../../../xpack_main/public/services/xpack_info';
|
||||
import { banners, addAppRedirectMessageToUrl } from 'ui/notify';
|
||||
import { LICENSE_TYPE } from '../../common/constants/license';
|
||||
import { LICENSE_STATUS_VALID } from '../../../../common/constants/license_status';
|
||||
|
||||
import chrome from 'ui/chrome';
|
||||
import { EuiCallOut } from '@elastic/eui';
|
||||
|
||||
let licenseHasExpired = true;
|
||||
let licenseType = null;
|
||||
let expiredLicenseBannerId;
|
||||
let licenseType: LICENSE_TYPE | null = null;
|
||||
let expiredLicenseBannerId: string;
|
||||
|
||||
export function checkFullLicense(kbnBaseUrl, kbnUrl) {
|
||||
export function checkFullLicense() {
|
||||
const features = getFeatures();
|
||||
licenseType = features.licenseType;
|
||||
|
||||
if (features.isAvailable === false) {
|
||||
// ML is not enabled
|
||||
return redirectToKibana(features, kbnBaseUrl);
|
||||
|
||||
return redirectToKibana();
|
||||
} else if (features.licenseType === LICENSE_TYPE.BASIC) {
|
||||
|
||||
// ML is enabled, but only with a basic or gold license
|
||||
return redirectToBasic(kbnUrl);
|
||||
|
||||
return redirectToBasic();
|
||||
} else {
|
||||
|
||||
// ML is enabled
|
||||
setLicenseExpired(features);
|
||||
return Promise.resolve(features);
|
||||
}
|
||||
}
|
||||
|
||||
export function checkBasicLicense(kbnBaseUrl) {
|
||||
export function checkBasicLicense() {
|
||||
const features = getFeatures();
|
||||
licenseType = features.licenseType;
|
||||
|
||||
if (features.isAvailable === false) {
|
||||
// ML is not enabled
|
||||
return redirectToKibana(features, kbnBaseUrl);
|
||||
|
||||
return redirectToKibana();
|
||||
} else {
|
||||
|
||||
// ML is enabled
|
||||
setLicenseExpired(features);
|
||||
return Promise.resolve(features);
|
||||
|
@ -58,38 +51,32 @@ export function checkBasicLicense(kbnBaseUrl) {
|
|||
// a wrapper for checkFullLicense which doesn't resolve if the license has expired.
|
||||
// this is used by all create jobs pages to redirect back to the jobs list
|
||||
// if the user's license has expired.
|
||||
export function checkLicenseExpired(kbnBaseUrl, kbnUrl) {
|
||||
return checkFullLicense(kbnBaseUrl, kbnUrl)
|
||||
.then((features) => {
|
||||
export function checkLicenseExpired() {
|
||||
return checkFullLicense()
|
||||
.then((features: any) => {
|
||||
if (features.hasExpired) {
|
||||
kbnUrl.redirect('/jobs');
|
||||
return Promise.halt();
|
||||
window.location.href = '#/jobs';
|
||||
return Promise.reject();
|
||||
} else {
|
||||
return Promise.resolve(features);
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
return Promise.halt();
|
||||
return Promise.reject();
|
||||
});
|
||||
}
|
||||
|
||||
function setLicenseExpired(features) {
|
||||
licenseHasExpired = (features.hasExpired || false);
|
||||
function setLicenseExpired(features: any) {
|
||||
licenseHasExpired = features.hasExpired || false;
|
||||
// If the license has expired ML app will still work for 7 days and then
|
||||
// the job management endpoints (e.g. create job, start datafeed) will be restricted.
|
||||
// Therefore we need to keep the app enabled but show an info banner to the user.
|
||||
if(licenseHasExpired) {
|
||||
if (licenseHasExpired) {
|
||||
const message = features.message;
|
||||
if (expiredLicenseBannerId === undefined) {
|
||||
// Only show the banner once with no way to dismiss it
|
||||
expiredLicenseBannerId = banners.add({
|
||||
component: (
|
||||
<EuiCallOut
|
||||
iconType="iInCircle"
|
||||
color="warning"
|
||||
title={message}
|
||||
/>
|
||||
),
|
||||
component: <EuiCallOut iconType="iInCircle" color="warning" title={message} />,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -99,16 +86,14 @@ function getFeatures() {
|
|||
return xpackInfo.get('features.ml');
|
||||
}
|
||||
|
||||
function redirectToKibana(features, kbnBaseUrl) {
|
||||
const { message } = features;
|
||||
const newUrl = addAppRedirectMessageToUrl(chrome.addBasePath(kbnBaseUrl), (message || ''));
|
||||
window.location.href = newUrl;
|
||||
return Promise.halt();
|
||||
function redirectToKibana() {
|
||||
window.location.href = '/';
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
function redirectToBasic(kbnUrl) {
|
||||
kbnUrl.redirect('/datavisualizer');
|
||||
return Promise.halt();
|
||||
function redirectToBasic() {
|
||||
window.location.href = '#/datavisualizer';
|
||||
return Promise.reject();
|
||||
}
|
||||
|
||||
export function hasLicenseExpired() {
|
||||
|
@ -116,10 +101,10 @@ export function hasLicenseExpired() {
|
|||
}
|
||||
|
||||
export function isFullLicense() {
|
||||
return (licenseType === LICENSE_TYPE.FULL);
|
||||
return licenseType === LICENSE_TYPE.FULL;
|
||||
}
|
||||
|
||||
export function xpackFeatureAvailable(feature) {
|
||||
export function xpackFeatureAvailable(feature: string) {
|
||||
// each plugin can register their own set of features.
|
||||
// so we need specific checks for each one.
|
||||
// this list can grow if we need to check other plugin's features.
|
|
@ -15,18 +15,18 @@ import { ACCESS_DENIED_PATH } from '../management/management_urls';
|
|||
|
||||
let privileges: Privileges = getDefaultPrivileges();
|
||||
// manage_ml requires all monitor and admin cluster privileges: https://github.com/elastic/elasticsearch/blob/664a29c8905d8ce9ba8c18aa1ed5c5de93a0eabc/x-pack/plugin/core/src/main/java/org/elasticsearch/xpack/core/security/authz/privilege/ClusterPrivilege.java#L53
|
||||
export function canGetManagementMlJobs(kbnUrl: any) {
|
||||
export function canGetManagementMlJobs() {
|
||||
return new Promise((resolve, reject) => {
|
||||
getManageMlPrivileges().then(
|
||||
({ capabilities, isPlatinumOrTrialLicense, mlFeatureEnabledInSpace }) => {
|
||||
privileges = capabilities;
|
||||
// Loop through all privilages to ensure they are all set to true.
|
||||
// Loop through all privileges to ensure they are all set to true.
|
||||
const isManageML = Object.values(privileges).every(p => p === true);
|
||||
|
||||
if (isManageML === true && isPlatinumOrTrialLicense === true) {
|
||||
return resolve({ mlFeatureEnabledInSpace });
|
||||
} else {
|
||||
kbnUrl.redirect(ACCESS_DENIED_PATH);
|
||||
window.location.href = ACCESS_DENIED_PATH;
|
||||
return reject();
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ export function canGetManagementMlJobs(kbnUrl: any) {
|
|||
});
|
||||
}
|
||||
|
||||
export function checkGetJobsPrivilege(kbnUrl: any): Promise<Privileges> {
|
||||
export function checkGetJobsPrivilege(): Promise<Privileges> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => {
|
||||
privileges = capabilities;
|
||||
|
@ -46,14 +46,14 @@ export function checkGetJobsPrivilege(kbnUrl: any): Promise<Privileges> {
|
|||
if (privileges.canGetJobs || isPlatinumOrTrialLicense === false) {
|
||||
return resolve(privileges);
|
||||
} else {
|
||||
kbnUrl.redirect('/access-denied');
|
||||
window.location.href = '#/access-denied';
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function checkCreateJobsPrivilege(kbnUrl: any): Promise<Privileges> {
|
||||
export function checkCreateJobsPrivilege(): Promise<Privileges> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPrivileges().then(({ capabilities, isPlatinumOrTrialLicense }) => {
|
||||
privileges = capabilities;
|
||||
|
@ -65,14 +65,14 @@ export function checkCreateJobsPrivilege(kbnUrl: any): Promise<Privileges> {
|
|||
} else {
|
||||
// if the user has no permission to create a job,
|
||||
// redirect them back to the Transforms Management page
|
||||
kbnUrl.redirect('/jobs');
|
||||
window.location.href = '#/jobs';
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function checkFindFileStructurePrivilege(kbnUrl: any): Promise<Privileges> {
|
||||
export function checkFindFileStructurePrivilege(): Promise<Privileges> {
|
||||
return new Promise((resolve, reject) => {
|
||||
getPrivileges().then(({ capabilities }) => {
|
||||
privileges = capabilities;
|
||||
|
@ -81,7 +81,7 @@ export function checkFindFileStructurePrivilege(kbnUrl: any): Promise<Privileges
|
|||
if (privileges.canFindFileStructure) {
|
||||
return resolve(privileges);
|
||||
} else {
|
||||
kbnUrl.redirect('/access-denied');
|
||||
window.location.href = '#/access-denied';
|
||||
return reject();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -4,21 +4,19 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import _ from 'lodash';
|
||||
|
||||
import { IndexPattern } from 'ui/index_patterns';
|
||||
import { mlFunctionToESAggregation } from '../../common/util/job_utils';
|
||||
import { getIndexPatternById } from '../util/index_utils';
|
||||
import { getIndexPatternById, getIndexPatternIdFromName } from '../util/index_utils';
|
||||
import { mlJobService } from '../services/job_service';
|
||||
|
||||
type FormatsByJobId = Record<string, any>;
|
||||
type IndexPatternIdsByJob = Record<string, any>;
|
||||
|
||||
// Service for accessing FieldFormat objects configured for a Kibana index pattern
|
||||
// for use in formatting the actual and typical values from anomalies.
|
||||
class FieldFormatService {
|
||||
constructor() {
|
||||
this.indexPatternIdsByJob = {};
|
||||
this.formatsByJob = {};
|
||||
}
|
||||
indexPatternIdsByJob: IndexPatternIdsByJob = {};
|
||||
formatsByJob: FormatsByJobId = {};
|
||||
|
||||
// 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
|
||||
|
@ -26,60 +24,57 @@ class FieldFormatService {
|
|||
// configured in the datafeed of each job.
|
||||
// Builds a map of Kibana FieldFormats (plugins/data/common/field_formats)
|
||||
// against detector index by job ID.
|
||||
populateFormats(jobIds, indexPatterns) {
|
||||
populateFormats(jobIds: string[]) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 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.
|
||||
// If a Kibana index pattern has not been created
|
||||
// for this index, then no custom field formatting will occur.
|
||||
_.each(jobIds, (jobId) => {
|
||||
jobIds.forEach(jobId => {
|
||||
const jobObj = mlJobService.getJob(jobId);
|
||||
const datafeedIndices = jobObj.datafeed_config.indices;
|
||||
const indexPattern = _.find(indexPatterns, (index) => {
|
||||
return _.find(datafeedIndices, (datafeedIndex) => {
|
||||
return index.get('title') === datafeedIndex;
|
||||
});
|
||||
});
|
||||
|
||||
// Check if index pattern has been configured to match the index in datafeed.
|
||||
if (indexPattern !== undefined) {
|
||||
this.indexPatternIdsByJob[jobId] = indexPattern.id;
|
||||
const id = getIndexPatternIdFromName(datafeedIndices.length ? datafeedIndices[0] : '');
|
||||
if (id !== null) {
|
||||
this.indexPatternIdsByJob[jobId] = id;
|
||||
}
|
||||
});
|
||||
|
||||
const promises = jobIds.map(jobId => Promise.all([
|
||||
this.getFormatsForJob(jobId)
|
||||
]));
|
||||
const promises = jobIds.map(jobId => Promise.all([this.getFormatsForJob(jobId)]));
|
||||
|
||||
Promise.all(promises).then((fmtsByJobByDetector) => {
|
||||
_.each(fmtsByJobByDetector, (formatsByDetector, index) => {
|
||||
this.formatsByJob[jobIds[index]] = formatsByDetector[0];
|
||||
Promise.all(promises)
|
||||
.then(fmtsByJobByDetector => {
|
||||
fmtsByJobByDetector.forEach((formatsByDetector, i) => {
|
||||
this.formatsByJob[jobIds[i]] = formatsByDetector[0];
|
||||
});
|
||||
|
||||
resolve(this.formatsByJob);
|
||||
})
|
||||
.catch(err => {
|
||||
reject({ formats: {}, err });
|
||||
});
|
||||
|
||||
resolve(this.formatsByJob);
|
||||
}).catch(err => {
|
||||
console.log('fieldFormatService error populating formats:', err);
|
||||
reject({ formats: {}, err });
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Return the FieldFormat to use for formatting values from
|
||||
// the detector from the job with the specified ID.
|
||||
getFieldFormat(jobId, detectorIndex) {
|
||||
return _.get(this.formatsByJob, [jobId, detectorIndex]);
|
||||
getFieldFormat(jobId: string, detectorIndex: number) {
|
||||
if (this.formatsByJob.hasOwnProperty(jobId)) {
|
||||
return this.formatsByJob[jobId][detectorIndex];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Utility for returning the FieldFormat from a full populated Kibana index pattern object
|
||||
// containing the list of fields by name with their formats.
|
||||
getFieldFormatFromIndexPattern(fullIndexPattern, fieldName, esAggName) {
|
||||
getFieldFormatFromIndexPattern(
|
||||
fullIndexPattern: IndexPattern,
|
||||
fieldName: string,
|
||||
esAggName: string
|
||||
) {
|
||||
// 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.
|
||||
let fieldFormat = undefined;
|
||||
let fieldFormat;
|
||||
if (esAggName !== 'cardinality') {
|
||||
const fieldList = _.get(fullIndexPattern, 'fields', []);
|
||||
const fieldList = fullIndexPattern.fields;
|
||||
const field = fieldList.getByName(fieldName);
|
||||
if (field !== undefined) {
|
||||
fieldFormat = field.format;
|
||||
|
@ -89,34 +84,34 @@ class FieldFormatService {
|
|||
return fieldFormat;
|
||||
}
|
||||
|
||||
getFormatsForJob(jobId) {
|
||||
getFormatsForJob(jobId: string): Promise<any[]> {
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
const jobObj = mlJobService.getJob(jobId);
|
||||
const detectors = jobObj.analysis_config.detectors || [];
|
||||
const formatsByDetector = {};
|
||||
const formatsByDetector: any[] = [];
|
||||
|
||||
const indexPatternId = this.indexPatternIdsByJob[jobId];
|
||||
if (indexPatternId !== undefined) {
|
||||
// Load the full index pattern configuration to obtain the formats of each field.
|
||||
getIndexPatternById(indexPatternId)
|
||||
.then((indexPatternData) => {
|
||||
.then(indexPatternData => {
|
||||
// Store the FieldFormat for each job by detector_index.
|
||||
const fieldList = _.get(indexPatternData, 'fields', []);
|
||||
_.each(detectors, (dtr) => {
|
||||
const fieldList = indexPatternData.fields;
|
||||
detectors.forEach(dtr => {
|
||||
const esAgg = mlFunctionToESAggregation(dtr.function);
|
||||
// distinct_count detectors should fall back to the default
|
||||
// formatter as the values are just counts.
|
||||
if (dtr.field_name !== undefined && esAgg !== 'cardinality') {
|
||||
const field = fieldList.getByName(dtr.field_name);
|
||||
if (field !== undefined) {
|
||||
formatsByDetector[dtr.detector_index] = field.format;
|
||||
formatsByDetector[dtr.detector_index!] = field.format;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
resolve(formatsByDetector);
|
||||
}).catch(err => {
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
} else {
|
|
@ -34,6 +34,7 @@ declare interface JobService {
|
|||
createResultsUrl(jobId: string[], start: number, end: number, location: string): string;
|
||||
getJobAndGroupIds(): ExistingJobsAndGroups;
|
||||
searchPreview(job: CombinedJob): Promise<SearchResponse<any>>;
|
||||
getJob(jobId: string): CombinedJob;
|
||||
}
|
||||
|
||||
export const mlJobService: JobService;
|
||||
|
|
|
@ -4,19 +4,13 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS } from '../breadcrumbs';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS } from '../breadcrumbs';
|
||||
|
||||
export function getSettingsBreadcrumbs() {
|
||||
// Whilst top level nav menu with tabs remains,
|
||||
// use root ML breadcrumb.
|
||||
return [
|
||||
ML_BREADCRUMB,
|
||||
ANOMALY_DETECTION_BREADCRUMB,
|
||||
SETTINGS
|
||||
];
|
||||
return [ML_BREADCRUMB, ANOMALY_DETECTION_BREADCRUMB, SETTINGS];
|
||||
}
|
||||
|
||||
export function getCalendarManagementBreadcrumbs() {
|
||||
|
@ -24,10 +18,10 @@ export function getCalendarManagementBreadcrumbs() {
|
|||
...getSettingsBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagementLabel', {
|
||||
defaultMessage: 'Calendar management'
|
||||
defaultMessage: 'Calendar management',
|
||||
}),
|
||||
href: '#/settings/calendars_list'
|
||||
}
|
||||
href: '#/settings/calendars_list',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -36,10 +30,10 @@ export function getCreateCalendarBreadcrumbs() {
|
|||
...getCalendarManagementBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.createLabel', {
|
||||
defaultMessage: 'Create'
|
||||
defaultMessage: 'Create',
|
||||
}),
|
||||
href: '#/settings/calendars_list/new_calendar'
|
||||
}
|
||||
href: '#/settings/calendars_list/new_calendar',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -48,10 +42,10 @@ export function getEditCalendarBreadcrumbs() {
|
|||
...getCalendarManagementBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.calendarManagement.editLabel', {
|
||||
defaultMessage: 'Edit'
|
||||
defaultMessage: 'Edit',
|
||||
}),
|
||||
href: '#/settings/calendars_list/edit_calendar'
|
||||
}
|
||||
href: '#/settings/calendars_list/edit_calendar',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -60,10 +54,10 @@ export function getFilterListsBreadcrumbs() {
|
|||
...getSettingsBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterListsLabel', {
|
||||
defaultMessage: 'Filter lists'
|
||||
defaultMessage: 'Filter lists',
|
||||
}),
|
||||
href: '#/settings/filter_lists'
|
||||
}
|
||||
href: '#/settings/filter_lists',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -72,10 +66,10 @@ export function getCreateFilterListBreadcrumbs() {
|
|||
...getFilterListsBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.createLabel', {
|
||||
defaultMessage: 'Create'
|
||||
defaultMessage: 'Create',
|
||||
}),
|
||||
href: '#/settings/filter_lists/new'
|
||||
}
|
||||
href: '#/settings/filter_lists/new',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -84,9 +78,9 @@ export function getEditFilterListBreadcrumbs() {
|
|||
...getFilterListsBreadcrumbs(),
|
||||
{
|
||||
text: i18n.translate('xpack.ml.settings.breadcrumbs.filterLists.editLabel', {
|
||||
defaultMessage: 'Edit'
|
||||
defaultMessage: 'Edit',
|
||||
}),
|
||||
href: '#/settings/filter_lists/edit'
|
||||
}
|
||||
href: '#/settings/filter_lists/edit',
|
||||
},
|
||||
];
|
||||
}
|
|
@ -4,21 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import 'ngreact';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { checkFullLicense } from '../../../license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
|
||||
import { checkMlNodesAvailable } from '../../../ml_nodes_check';
|
||||
import { getCreateCalendarBreadcrumbs, getEditCalendarBreadcrumbs } from '../../breadcrumbs';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { NewCalendar } from './new_calendar';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
|
@ -33,7 +34,7 @@ uiRoutes
|
|||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
checkMlNodesAvailable,
|
||||
}
|
||||
},
|
||||
})
|
||||
.when('/settings/calendars_list/edit_calendar/:calendarId', {
|
||||
template,
|
||||
|
@ -42,21 +43,19 @@ uiRoutes
|
|||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
checkMlNodesAvailable,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
import { NewCalendar } from './new_calendar.js';
|
||||
|
||||
module.directive('mlNewCalendar', function ($route) {
|
||||
module.directive('mlNewCalendar', function($route: any) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {},
|
||||
link: function (scope, element) {
|
||||
link(scope: ng.IScope, element: ng.IAugmentedJQuery) {
|
||||
const props = {
|
||||
calendarId: $route.current.params.calendarId,
|
||||
canCreateCalendar: checkPermission('canCreateCalendar'),
|
||||
canDeleteCalendar: checkPermission('canDeleteCalendar')
|
||||
canDeleteCalendar: checkPermission('canDeleteCalendar'),
|
||||
};
|
||||
|
||||
ReactDOM.render(
|
||||
|
@ -65,6 +64,6 @@ module.directive('mlNewCalendar', function ($route) {
|
|||
</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
|
@ -4,5 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import './directive';
|
13
x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts
vendored
Normal file
13
x-pack/legacy/plugins/ml/public/settings/calendars/edit/new_calendar.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { FC } from 'react';
|
||||
|
||||
declare const NewCalendar: FC<{
|
||||
calendarId: string;
|
||||
canCreateCalendar: boolean;
|
||||
canDeleteCalendar: boolean;
|
||||
}>;
|
12
x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts
vendored
Normal file
12
x-pack/legacy/plugins/ml/public/settings/calendars/list/calendars_list.d.ts
vendored
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { FC } from 'react';
|
||||
|
||||
declare const CalendarsList: FC<{
|
||||
canCreateCalendar: boolean;
|
||||
canDeleteCalendar: boolean;
|
||||
}>;
|
|
@ -8,16 +8,18 @@ import 'ngreact';
|
|||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { checkFullLicense } from '../../../license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { getCalendarManagementBreadcrumbs } from '../../breadcrumbs';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { CalendarsList } from './calendars_list';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
|
@ -34,14 +36,12 @@ uiRoutes.when('/settings/calendars_list', {
|
|||
},
|
||||
});
|
||||
|
||||
import { CalendarsList } from './calendars_list';
|
||||
|
||||
module.directive('mlCalendarsList', function () {
|
||||
module.directive('mlCalendarsList', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {},
|
||||
link: function (scope, element) {
|
||||
link(scope: ng.IScope, element: ng.IAugmentedJQuery) {
|
||||
const props = {
|
||||
canCreateCalendar: checkPermission('canCreateCalendar'),
|
||||
canDeleteCalendar: checkPermission('canDeleteCalendar'),
|
|
@ -4,6 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import './directive';
|
|
@ -4,22 +4,22 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import 'ngreact';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { getCreateFilterListBreadcrumbs, getEditFilterListBreadcrumbs } from '../../breadcrumbs';
|
||||
import { checkFullLicense } from 'plugins/ml/license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||
import { EditFilterList } from './edit_filter_list';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
|
||||
import { checkFullLicense } from '../../../license/check_license';
|
||||
import { getCreateFilterListBreadcrumbs, getEditFilterListBreadcrumbs } from '../../breadcrumbs';
|
||||
|
||||
import { EditFilterList } from './edit_filter_list';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
|
@ -34,7 +34,7 @@ uiRoutes
|
|||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
}
|
||||
},
|
||||
})
|
||||
.when('/settings/filter_lists/edit_filter_list/:filterId', {
|
||||
template,
|
||||
|
@ -43,15 +43,15 @@ uiRoutes
|
|||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
module.directive('mlEditFilterList', function ($route) {
|
||||
module.directive('mlEditFilterList', function($route: any) {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {},
|
||||
link: function (scope, element) {
|
||||
link(scope: ng.IScope, element: ng.IAugmentedJQuery) {
|
||||
const props = {
|
||||
filterId: $route.current.params.filterId,
|
||||
canCreateFilter: checkPermission('canCreateFilter'),
|
||||
|
@ -64,6 +64,6 @@ module.directive('mlEditFilterList', function ($route) {
|
|||
</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
13
x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts
vendored
Normal file
13
x-pack/legacy/plugins/ml/public/settings/filter_lists/edit/edit_filter_list.d.ts
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* 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 { FC } from 'react';
|
||||
|
||||
declare const EditFilterList: FC<{
|
||||
filterId: string;
|
||||
canCreateFilter: boolean;
|
||||
canDeleteFilter: boolean;
|
||||
}>;
|
|
@ -4,6 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import './directive';
|
|
@ -4,6 +4,5 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import './edit';
|
||||
import './list';
|
|
@ -4,45 +4,44 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import 'ngreact';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { getFilterListsBreadcrumbs } from '../../breadcrumbs';
|
||||
import { checkFullLicense } from 'plugins/ml/license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from 'plugins/ml/privilege/check_privilege';
|
||||
import { getMlNodeCount } from 'plugins/ml/ml_nodes_check/check_ml_nodes';
|
||||
import { FilterLists } from './filter_lists';
|
||||
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import { checkFullLicense } from '../../../license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../../../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../../../ml_nodes_check/check_ml_nodes';
|
||||
import { getFilterListsBreadcrumbs } from '../../breadcrumbs';
|
||||
|
||||
import { FilterLists } from './filter_lists';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
<ml-filter-lists />
|
||||
`;
|
||||
|
||||
uiRoutes
|
||||
.when('/settings/filter_lists', {
|
||||
template,
|
||||
k7Breadcrumbs: getFilterListsBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
}
|
||||
});
|
||||
uiRoutes.when('/settings/filter_lists', {
|
||||
template,
|
||||
k7Breadcrumbs: getFilterListsBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
},
|
||||
});
|
||||
|
||||
module.directive('mlFilterLists', function () {
|
||||
module.directive('mlFilterLists', function() {
|
||||
return {
|
||||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {},
|
||||
link: function (scope, element) {
|
||||
link(scope: ng.IScope, element: ng.IAugmentedJQuery) {
|
||||
const props = {
|
||||
canCreateFilter: checkPermission('canCreateFilter'),
|
||||
canDeleteFilter: checkPermission('canDeleteFilter'),
|
||||
|
@ -54,6 +53,6 @@ module.directive('mlFilterLists', function () {
|
|||
</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
|
@ -4,6 +4,9 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { Duration } from 'moment';
|
||||
import { FC } from 'react';
|
||||
|
||||
export function parseInterval(interval: string): Duration;
|
||||
declare const FilterLists: FC<{
|
||||
canCreateFilter: boolean;
|
||||
canDeleteFilter: boolean;
|
||||
}>;
|
|
@ -4,6 +4,4 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import './directive';
|
|
@ -4,8 +4,6 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import './settings_directive';
|
||||
import './calendars';
|
||||
import './filter_lists';
|
|
@ -4,44 +4,41 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import 'ngreact';
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { uiModules } from 'ui/modules';
|
||||
const module = uiModules.get('apps/ml', ['react']);
|
||||
|
||||
import { checkFullLicense } from '../license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes';
|
||||
import { getSettingsBreadcrumbs } from './breadcrumbs';
|
||||
|
||||
import { I18nContext } from 'ui/i18n';
|
||||
import uiRoutes from 'ui/routes';
|
||||
import { timefilter } from 'ui/timefilter';
|
||||
import { checkFullLicense } from '../license/check_license';
|
||||
import { checkGetJobsPrivilege, checkPermission } from '../privilege/check_privilege';
|
||||
import { getMlNodeCount } from '../ml_nodes_check/check_ml_nodes';
|
||||
import { getSettingsBreadcrumbs } from './breadcrumbs';
|
||||
|
||||
const template = `
|
||||
<div class="euiSpacer euiSpacer--s" />
|
||||
<ml-settings />
|
||||
`;
|
||||
|
||||
uiRoutes
|
||||
.when('/settings', {
|
||||
template,
|
||||
k7Breadcrumbs: getSettingsBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
}
|
||||
});
|
||||
|
||||
uiRoutes.when('/settings', {
|
||||
template,
|
||||
k7Breadcrumbs: getSettingsBreadcrumbs,
|
||||
resolve: {
|
||||
CheckLicense: checkFullLicense,
|
||||
privileges: checkGetJobsPrivilege,
|
||||
mlNodeCount: getMlNodeCount,
|
||||
},
|
||||
});
|
||||
|
||||
// @ts-ignore
|
||||
import { Settings } from './settings.js';
|
||||
|
||||
module.directive('mlSettings', function () {
|
||||
|
||||
module.directive('mlSettings', function() {
|
||||
const canGetFilters = checkPermission('canGetFilters');
|
||||
const canGetCalendars = checkPermission('canGetCalendars');
|
||||
|
||||
|
@ -49,7 +46,7 @@ module.directive('mlSettings', function () {
|
|||
restrict: 'E',
|
||||
replace: false,
|
||||
scope: {},
|
||||
link: function (scope, element) {
|
||||
link(scope: ng.IScope, element: ng.IAugmentedJQuery) {
|
||||
timefilter.disableTimeRangeSelector();
|
||||
timefilter.disableAutoRefreshSelector();
|
||||
|
||||
|
@ -59,6 +56,6 @@ module.directive('mlSettings', function () {
|
|||
</I18nContext>,
|
||||
element[0]
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
});
|
|
@ -68,7 +68,6 @@ import { mlJobService } from '../services/job_service';
|
|||
import { mlResultsService } from '../services/results_service';
|
||||
import { mlTimefilterRefresh$ } from '../services/timefilter_refresh_service';
|
||||
|
||||
import { getIndexPatterns } from '../util/index_utils';
|
||||
import { getBoundsRoundedToInterval } from '../util/time_buckets';
|
||||
|
||||
import { APP_STATE_ACTION, CHARTS_POINT_TARGET, TIME_FIELD_NAME } from './timeseriesexplorer_constants';
|
||||
|
@ -827,7 +826,7 @@ export class TimeSeriesExplorer extends React.Component {
|
|||
() => {
|
||||
this.updateControlsForDetector(() => {
|
||||
// Populate the map of jobs / detectors / field formatters for the selected IDs and refresh.
|
||||
mlFieldFormatService.populateFormats([jobId], getIndexPatterns())
|
||||
mlFieldFormatService.populateFormats([jobId])
|
||||
.catch((err) => { console.log('Error populating field formats:', err); })
|
||||
// Load the data - if the FieldFormats failed to populate
|
||||
// the default formatting will be used for metric values.
|
||||
|
|
|
@ -17,8 +17,6 @@ type IndexPatternSavedObject = SimpleSavedObject<SavedObjectAttributes>;
|
|||
let indexPatternCache: IndexPatternSavedObject[] = [];
|
||||
let fullIndexPatterns: IndexPatterns | null = null;
|
||||
|
||||
export let refreshIndexPatterns: (() => Promise<IndexPatternSavedObject[]>) | null = null;
|
||||
|
||||
export function loadIndexPatterns() {
|
||||
fullIndexPatterns = data.indexPatterns.indexPatterns;
|
||||
const savedObjectsClient = chrome.getSavedObjectsClient();
|
||||
|
@ -30,20 +28,6 @@ export function loadIndexPatterns() {
|
|||
})
|
||||
.then(response => {
|
||||
indexPatternCache = response.savedObjects;
|
||||
if (refreshIndexPatterns === null) {
|
||||
refreshIndexPatterns = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
loadIndexPatterns()
|
||||
.then(resp => {
|
||||
resolve(resp);
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
return indexPatternCache;
|
||||
});
|
||||
}
|
||||
|
@ -62,7 +46,7 @@ export function getIndexPatternIdFromName(name: string) {
|
|||
return indexPatternCache[j].id;
|
||||
}
|
||||
}
|
||||
return name;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function loadCurrentIndexPattern(indexPatterns: IndexPatterns, $route: Record<string, any>) {
|
||||
|
|
|
@ -4,20 +4,18 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
|
||||
import expect from '@kbn/expect';
|
||||
import sinon from 'sinon';
|
||||
import { set } from 'lodash';
|
||||
import { XPackInfo } from '../../../../../../../legacy/plugins/xpack_main/server/lib/xpack_info';
|
||||
import { checkLicense } from '../check_license';
|
||||
|
||||
describe('check_license', () => {
|
||||
|
||||
let mockLicenseInfo;
|
||||
beforeEach(() => mockLicenseInfo = {});
|
||||
let mockLicenseInfo: XPackInfo;
|
||||
beforeEach(() => (mockLicenseInfo = {} as XPackInfo));
|
||||
|
||||
describe('license information is undefined', () => {
|
||||
beforeEach(() => mockLicenseInfo = undefined);
|
||||
beforeEach(() => (mockLicenseInfo = {} as XPackInfo));
|
||||
|
||||
it('should set isAvailable to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
|
||||
|
@ -37,7 +35,7 @@ describe('check_license', () => {
|
|||
});
|
||||
|
||||
describe('license information is not available', () => {
|
||||
beforeEach(() => mockLicenseInfo.isAvailable = () => false);
|
||||
beforeEach(() => (mockLicenseInfo.isAvailable = () => false));
|
||||
|
||||
it('should set isAvailable to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
|
||||
|
@ -64,14 +62,21 @@ describe('check_license', () => {
|
|||
|
||||
describe('& ML is disabled in Elasticsearch', () => {
|
||||
beforeEach(() => {
|
||||
set(mockLicenseInfo, 'feature', sinon.stub().withArgs('ml').returns({ isEnabled: () => false }));
|
||||
set(
|
||||
mockLicenseInfo,
|
||||
'feature',
|
||||
sinon
|
||||
.stub()
|
||||
.withArgs('ml')
|
||||
.returns({ isEnabled: () => false })
|
||||
);
|
||||
});
|
||||
|
||||
it ('should set showLinks to false', () => {
|
||||
it('should set showLinks to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showLinks).to.be(false);
|
||||
});
|
||||
|
||||
it ('should set isAvailable to false', () => {
|
||||
it('should set isAvailable to false', () => {
|
||||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(false);
|
||||
});
|
||||
|
||||
|
@ -86,7 +91,14 @@ describe('check_license', () => {
|
|||
|
||||
describe('& ML is enabled in Elasticsearch', () => {
|
||||
beforeEach(() => {
|
||||
set(mockLicenseInfo, 'feature', sinon.stub().withArgs('ml').returns({ isEnabled: () => true }));
|
||||
set(
|
||||
mockLicenseInfo,
|
||||
'feature',
|
||||
sinon
|
||||
.stub()
|
||||
.withArgs('ml')
|
||||
.returns({ isEnabled: () => true })
|
||||
);
|
||||
});
|
||||
|
||||
describe('& license is trial or platinum', () => {
|
||||
|
@ -99,11 +111,11 @@ describe('check_license', () => {
|
|||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
|
||||
});
|
||||
|
||||
it ('should set showLinks to true', () => {
|
||||
it('should set showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
|
||||
});
|
||||
|
||||
it ('should set enableLinks to true', () => {
|
||||
it('should set enableLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
|
||||
});
|
||||
|
||||
|
@ -119,11 +131,11 @@ describe('check_license', () => {
|
|||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
|
||||
});
|
||||
|
||||
it ('should set showLinks to true', () => {
|
||||
it('should set showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
|
||||
});
|
||||
|
||||
it ('should set enableLinks to true', () => {
|
||||
it('should set enableLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).enableLinks).to.be(true);
|
||||
});
|
||||
|
||||
|
@ -143,7 +155,7 @@ describe('check_license', () => {
|
|||
expect(checkLicense(mockLicenseInfo).isAvailable).to.be(true);
|
||||
});
|
||||
|
||||
it ('should set showLinks to true', () => {
|
||||
it('should set showLinks to true', () => {
|
||||
expect(checkLicense(mockLicenseInfo).showLinks).to.be(true);
|
||||
});
|
||||
});
|
|
@ -1,17 +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.
|
||||
*/
|
||||
|
||||
import { LICENSE_TYPE } from '../../../common/constants/license';
|
||||
interface Response {
|
||||
isAvailable: boolean;
|
||||
showLinks: boolean;
|
||||
enableLinks: boolean;
|
||||
licenseType: LICENSE_TYPE;
|
||||
hasExpired: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function checkLicense(xpackLicenseInfo: any): Response;
|
|
@ -4,11 +4,20 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
|
||||
import { LICENSE_TYPE } from '../../../common/constants/license';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { LICENSE_TYPE } from '../../../common/constants/license';
|
||||
import { XPackInfo } from '../../../../../../legacy/plugins/xpack_main/server/lib/xpack_info';
|
||||
|
||||
export function checkLicense(xpackLicenseInfo) {
|
||||
interface Response {
|
||||
isAvailable: boolean;
|
||||
showLinks: boolean;
|
||||
enableLinks: boolean;
|
||||
licenseType?: LICENSE_TYPE;
|
||||
hasExpired?: boolean;
|
||||
message?: string;
|
||||
}
|
||||
|
||||
export function checkLicense(xpackLicenseInfo: XPackInfo): Response {
|
||||
// If, for some reason, we cannot get the license information
|
||||
// from Elasticsearch, assume worst case and disable the Machine Learning UI
|
||||
if (!xpackLicenseInfo || !xpackLicenseInfo.isAvailable()) {
|
||||
|
@ -16,9 +25,13 @@ export function checkLicense(xpackLicenseInfo) {
|
|||
isAvailable: false,
|
||||
showLinks: true,
|
||||
enableLinks: false,
|
||||
message: i18n.translate('xpack.ml.checkLicense.licenseInformationNotAvailableThisTimeMessage', {
|
||||
defaultMessage: 'You cannot use Machine Learning because license information is not available at this time.'
|
||||
})
|
||||
message: i18n.translate(
|
||||
'xpack.ml.checkLicense.licenseInformationNotAvailableThisTimeMessage',
|
||||
{
|
||||
defaultMessage:
|
||||
'You cannot use Machine Learning because license information is not available at this time.',
|
||||
}
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -29,18 +42,15 @@ export function checkLicense(xpackLicenseInfo) {
|
|||
showLinks: false,
|
||||
enableLinks: false,
|
||||
message: i18n.translate('xpack.ml.checkLicense.mlIsUnavailableMessage', {
|
||||
defaultMessage: 'Machine Learning is unavailable'
|
||||
})
|
||||
defaultMessage: 'Machine Learning is unavailable',
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
||||
const VALID_FULL_LICENSE_MODES = [
|
||||
'trial',
|
||||
'platinum'
|
||||
];
|
||||
const VALID_FULL_LICENSE_MODES = ['trial', 'platinum'];
|
||||
|
||||
const isLicenseModeValid = xpackLicenseInfo.license.isOneOf(VALID_FULL_LICENSE_MODES);
|
||||
const licenseType = (isLicenseModeValid === true) ? LICENSE_TYPE.FULL : LICENSE_TYPE.BASIC;
|
||||
const licenseType = isLicenseModeValid === true ? LICENSE_TYPE.FULL : LICENSE_TYPE.BASIC;
|
||||
const isLicenseActive = xpackLicenseInfo.license.isActive();
|
||||
const licenseTypeName = xpackLicenseInfo.license.getType();
|
||||
|
||||
|
@ -54,8 +64,8 @@ export function checkLicense(xpackLicenseInfo) {
|
|||
licenseType,
|
||||
message: i18n.translate('xpack.ml.checkLicense.licenseHasExpiredMessage', {
|
||||
defaultMessage: 'Your {licenseTypeName} Machine Learning license has expired.',
|
||||
values: { licenseTypeName }
|
||||
})
|
||||
values: { licenseTypeName },
|
||||
}),
|
||||
};
|
||||
}
|
||||
|
|
@ -9,7 +9,7 @@
|
|||
import { estimateBucketSpanFactory } from '../../models/bucket_span_estimator';
|
||||
import { mlFunctionToESAggregation } from '../../../common/util/job_utils';
|
||||
import { SKIP_BUCKET_SPAN_ESTIMATION } from '../../../common/constants/validation';
|
||||
import { parseInterval } from '../../../common/util/parse_interval.js';
|
||||
import { parseInterval } from '../../../common/util/parse_interval';
|
||||
|
||||
import { validateJobObject } from './validate_job_object';
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import _ from 'lodash';
|
||||
|
||||
import { ES_FIELD_TYPES } from '../../../../../../../src/plugins/data/server';
|
||||
import { parseInterval } from '../../../common/util/parse_interval.js';
|
||||
import { parseInterval } from '../../../common/util/parse_interval';
|
||||
import { validateJobObject } from './validate_job_object';
|
||||
|
||||
const BUCKET_SPAN_COMPARE_FACTOR = 25;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue