[ML] Adding shared services to ml setup contract (#59730)

* [ML] Adding shared services to ml setup contract

* adding data recognizer

* typescripting js client

* adding results service

* code clean up

* adding generic ml index search

* making cloud optional
This commit is contained in:
James Gowdy 2020-03-12 10:04:40 +00:00 committed by GitHub
parent ed68ede2b6
commit d5c092811b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
70 changed files with 511 additions and 173 deletions

View file

@ -0,0 +1,32 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { CategorizationAnalyzer } from './categories';
export interface MlServerDefaults {
anomaly_detectors: {
categorization_examples_limit?: number;
model_memory_limit?: string;
model_snapshot_retention_days?: number;
categorization_analyzer?: CategorizationAnalyzer;
};
datafeeds: { scroll_size?: number };
}
export interface MlServerLimits {
max_model_memory_limit?: string;
}
export interface MlInfoResponse {
defaults: MlServerDefaults;
limits: MlServerLimits;
native_code: {
build_hash: string;
version: string;
};
upgrade_mode: boolean;
cloudId?: string;
}

View file

@ -3,7 +3,7 @@
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObjectAttributes } from 'src/core/public';
import { SavedObjectAttributes } from 'kibana/public';
import { Datafeed, Job } from '../types/anomaly_detection_jobs';
export interface ModuleJob {

View file

@ -10,7 +10,7 @@ import { Server } from 'src/legacy/server/kbn_server';
import { DEFAULT_APP_CATEGORIES } from '../../../../src/core/utils';
// @ts-ignore: could not find declaration file for module
import { mirrorPluginStatus } from '../../server/lib/mirror_plugin_status';
// @ts-ignore: could not find declaration file for module
// @ts-ignore: importing JSON file
import mappings from './mappings';
export const ml = (kibana: any) => {

View file

@ -8,7 +8,7 @@ import { useReducer } from 'react';
import { i18n } from '@kbn/i18n';
import { SimpleSavedObject } from 'src/core/public';
import { SimpleSavedObject } from 'kibana/public';
import { ml } from '../../../../../services/ml_api_service';
import { useMlContext } from '../../../../../contexts/ml';

View file

@ -5,7 +5,7 @@
*/
import React, { FC, Fragment } from 'react';
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient } from 'kibana/public';
import { useTimefilter } from '../../contexts/kibana';
import { NavigationMenu } from '../../components/navigation_menu';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient } from 'kibana/public';
import { esQuery, Query, esKuery } from '../../../../../../../../../src/plugins/data/public';
import { IIndexPattern } from '../../../../../../../../../src/plugins/data/common/index_patterns';
import { SEARCH_QUERY_LANGUAGE } from '../../../../../common/constants/search';

View file

@ -5,7 +5,7 @@
*/
import { i18n } from '@kbn/i18n';
import { ChromeBreadcrumb } from '../../../../../../../src/core/public';
import { ChromeBreadcrumb } from 'kibana/public';
export const ML_BREADCRUMB: ChromeBreadcrumb = Object.freeze({
text: i18n.translate('xpack.ml.machineLearningBreadcrumbLabel', {

View file

@ -8,7 +8,7 @@ import React, { FC } from 'react';
import { HashRouter, Route, RouteProps } from 'react-router-dom';
import { Location } from 'history';
import { IUiSettingsClient, ChromeStart } from 'src/core/public';
import { IUiSettingsClient, ChromeStart } from 'kibana/public';
import { ChromeBreadcrumb } from 'kibana/public';
import { IndexPatternsContract } from 'src/plugins/data/public';
import { MlContext, MlContextValue } from '../contexts/ml';

View file

@ -5,7 +5,7 @@
*/
import { useEffect, useState } from 'react';
import { IUiSettingsClient } from 'src/core/public';
import { IUiSettingsClient } from 'kibana/public';
import {
getIndexPatternById,
getIndexPatternsContract,

View file

@ -11,7 +11,11 @@ import { AggFieldNamePair } from '../../../../common/types/fields';
import { Category } from '../../../../common/types/categories';
import { ExistingJobsAndGroups } from '../job_service';
import { PrivilegesResponse } from '../../../../common/types/privileges';
import { MlServerDefaults, MlServerLimits } from '../ml_server_info';
import {
MlInfoResponse,
MlServerDefaults,
MlServerLimits,
} from '../../../../common/types/ml_server_info';
import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types';
import { DataFrameAnalyticsStats } from '../../data_frame_analytics/pages/analytics_management/components/analytics_list/common';
import { JobMessage } from '../../../../common/types/audit_message';
@ -69,17 +73,6 @@ export interface BucketSpanEstimatorResponse {
message?: { msg: string } | string;
}
export interface MlInfoResponse {
defaults: MlServerDefaults;
limits: MlServerLimits;
native_code: {
build_hash: string;
version: string;
};
upgrade_mode: boolean;
cloudId?: string;
}
export interface SuccessCardinality {
id: 'success_cardinality';
}

View file

@ -5,21 +5,7 @@
*/
import { ml } from './ml_api_service';
import { CategorizationAnalyzer } from '../../../common/types/categories';
export interface MlServerDefaults {
anomaly_detectors: {
categorization_examples_limit?: number;
model_memory_limit?: string;
model_snapshot_retention_days?: number;
categorization_analyzer?: CategorizationAnalyzer;
};
datafeeds: { scroll_size?: number };
}
export interface MlServerLimits {
max_model_memory_limit?: string;
}
import { MlServerDefaults, MlServerLimits } from '../../../common/types/ml_server_info';
export interface CloudInfo {
cloudId: string | null;

View file

@ -11,7 +11,7 @@ import {
SavedObjectsClientContract,
ApplicationStart,
HttpStart,
} from 'src/core/public';
} from 'kibana/public';
import { IndexPatternsContract, DataPublicPluginStart } from 'src/plugins/data/public';
import {
DocLinksStart,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { PluginInitializer } from '../../../../../src/core/public';
import { PluginInitializer } from 'kibana/public';
import { MlPlugin, Setup, Start } from './plugin';
export const plugin: PluginInitializer<Setup, Start> = () => new MlPlugin();

View file

@ -5,7 +5,7 @@
*/
import { npSetup, npStart } from 'ui/new_platform';
import { PluginInitializerContext } from 'src/core/public';
import { PluginInitializerContext } from 'kibana/public';
import { SecurityPluginSetup } from '../../../../plugins/security/public';
import { LicensingPluginSetup } from '../../../../plugins/licensing/public';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Plugin, CoreStart, CoreSetup } from 'src/core/public';
import { Plugin, CoreStart, CoreSetup } from 'kibana/public';
import { MlDependencies } from './application/app';
export class MlPlugin implements Plugin<Setup, Start> {

View file

@ -4,14 +4,22 @@
* you may not use this file except in compliance with the Elastic License.
*/
import expect from '@kbn/expect';
import { elasticsearchJsPlugin } from '../elasticsearch_ml';
import { elasticsearchJsPlugin } from './elasticsearch_ml';
interface Endpoint {
fmt: string;
}
interface ClientAction {
urls?: Endpoint[];
url: Endpoint;
}
describe('ML - Endpoints', () => {
// Check all paths in the ML elasticsearchJsPlugin start with a leading forward slash
// so they work if Kibana is run behind a reverse proxy
const PATH_START = '/';
const urls = [];
const PATH_START: string = '/';
const urls: string[] = [];
// Stub objects
const Client = {
@ -20,7 +28,7 @@ describe('ML - Endpoints', () => {
const components = {
clientAction: {
factory: function(obj) {
factory(obj: ClientAction) {
// add each endpoint URL to a list
if (obj.urls) {
obj.urls.forEach(url => {
@ -45,7 +53,7 @@ describe('ML - Endpoints', () => {
describe('paths', () => {
it(`should start with ${PATH_START}`, () => {
urls.forEach(url => {
expect(url[0]).to.eql(PATH_START);
expect(url[0]).toEqual(PATH_START);
});
});
});

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
export const elasticsearchJsPlugin = (Client, config, components) => {
export const elasticsearchJsPlugin = (Client: any, config: any, components: any) => {
const ca = components.clientAction.factory;
Client.prototype.ml = components.clientAction.namespaceFactory();

View file

@ -5,7 +5,7 @@
*/
import { boomify, isBoom } from 'boom';
import { ResponseError, CustomHttpResponseOptions } from 'src/core/server';
import { ResponseError, CustomHttpResponseOptions } from 'kibana/server';
export function wrapError(error: any): CustomHttpResponseOptions<ResponseError> {
const boom = isBoom(error) ? error : boomify(error, { statusCode: error.status });

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Logger } from '../../../../../src/core/server';
import { Logger } from 'kibana/server';
export interface LogInitialization {
log: Logger;

View file

@ -6,5 +6,6 @@
import { PluginInitializerContext } from 'kibana/server';
import { MlServerPlugin } from './plugin';
export { MlStartContract, MlSetupContract } from './plugin';
export const plugin = (ctx: PluginInitializerContext) => new MlServerPlugin(ctx);

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { mlLog } from '../../client/log';
import {

View file

@ -16,7 +16,7 @@ import { mlPrivileges } from './privileges';
type ClusterPrivilege = Record<string, boolean>;
interface Response {
export interface MlCapabilities {
capabilities: Privileges;
upgradeInProgress: boolean;
isPlatinumOrTrialLicense: boolean;
@ -30,7 +30,7 @@ export function privilegesProvider(
ignoreSpaces: boolean = false
) {
const { isUpgradeInProgress } = upgradeCheckProvider(callAsCurrentUser);
async function getPrivileges(): Promise<Response> {
async function getPrivileges(): Promise<MlCapabilities> {
// get the default privileges, forced to be false.
const privileges = getDefaultPrivileges();

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { privilegesProvider } from './check_privileges';
export { privilegesProvider, MlCapabilities } from './check_privileges';

View file

@ -8,7 +8,7 @@ import {
KibanaResponseFactory,
RequestHandler,
RequestHandlerContext,
} from 'src/core/server';
} from 'kibana/server';
import { MlLicense } from '../../../../../legacy/plugins/ml/common/license';

View file

@ -5,7 +5,7 @@
*/
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { SavedObjectsServiceStart } from 'src/core/server';
import { SavedObjectsServiceStart } from 'kibana/server';
import {
createMlTelemetry,
ML_TELEMETRY_DOC_ID,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObjectAttributes, SavedObjectsClientContract } from 'src/core/server';
import { SavedObjectAttributes, SavedObjectsClientContract } from 'kibana/server';
export interface MlTelemetry extends SavedObjectAttributes {
file_data_visualizer: {
@ -40,7 +40,10 @@ export async function incrementFileDataVisualizerIndexCreationCount(
savedObjectsClient: SavedObjectsClientContract
): Promise<void> {
try {
const { attributes } = await savedObjectsClient.get('telemetry', 'telemetry');
const { attributes } = await savedObjectsClient.get<{ enabled: boolean }>(
'telemetry',
'telemetry'
);
if (attributes.enabled === false) {
return;

View file

@ -4,15 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Request } from 'hapi';
import { Legacy } from 'kibana';
import { KibanaRequest } from 'kibana/server';
import { Space, SpacesPluginSetup } from '../../../spaces/server';
export type RequestFacade = KibanaRequest | Legacy.Request;
interface GetActiveSpaceResponse {
valid: boolean;
space?: Space;
}
export function spacesUtilsProvider(spacesPlugin: SpacesPluginSetup, request: Request) {
export function spacesUtilsProvider(spacesPlugin: SpacesPluginSetup, request: RequestFacade) {
async function activeSpace(): Promise<GetActiveSpaceResponse> {
try {
return {

View file

@ -6,7 +6,7 @@
import getAnnotationsRequestMock from './__mocks__/get_annotations_request.json';
import getAnnotationsResponseMock from './__mocks__/get_annotations_response.json';
import { RequestHandlerContext } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { ANNOTATION_TYPE } from '../../../../../legacy/plugins/ml/common/constants/annotations';
import { ML_ANNOTATIONS_INDEX_ALIAS_WRITE } from '../../../../../legacy/plugins/ml/common/constants/index_patterns';
@ -26,27 +26,21 @@ describe('annotation_service', () => {
let callWithRequestSpy: any;
beforeEach(() => {
callWithRequestSpy = ({
ml: {
mlClient: {
callAsCurrentUser: jest.fn((action: string) => {
switch (action) {
case 'delete':
case 'index':
return Promise.resolve(acknowledgedResponseMock);
case 'search':
return Promise.resolve(getAnnotationsResponseMock);
}
}),
},
},
} as unknown) as RequestHandlerContext;
callWithRequestSpy = (jest.fn((action: string) => {
switch (action) {
case 'delete':
case 'index':
return Promise.resolve(acknowledgedResponseMock);
case 'search':
return Promise.resolve(getAnnotationsResponseMock);
}
}) as unknown) as APICaller;
});
describe('deleteAnnotation()', () => {
it('should delete annotation', async done => {
const { deleteAnnotation } = annotationServiceProvider(callWithRequestSpy);
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
const mockFunct = callWithRequestSpy;
const annotationMockId = 'mockId';
const deleteParamsMock: DeleteParams = {
@ -67,7 +61,7 @@ describe('annotation_service', () => {
describe('getAnnotation()', () => {
it('should get annotations for specific job', async done => {
const { getAnnotations } = annotationServiceProvider(callWithRequestSpy);
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
const mockFunct = callWithRequestSpy;
const indexAnnotationArgsMock: IndexAnnotationArgs = {
jobIds: [jobIdMock],
@ -93,15 +87,9 @@ describe('annotation_service', () => {
message: 'mock error message',
};
const callWithRequestSpyError = ({
ml: {
mlClient: {
callAsCurrentUser: jest.fn(() => {
return Promise.resolve(mockEsError);
}),
},
},
} as unknown) as RequestHandlerContext;
const callWithRequestSpyError = (jest.fn(() => {
return Promise.resolve(mockEsError);
}) as unknown) as APICaller;
const { getAnnotations } = annotationServiceProvider(callWithRequestSpyError);
@ -121,7 +109,7 @@ describe('annotation_service', () => {
describe('indexAnnotation()', () => {
it('should index annotation', async done => {
const { indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
const mockFunct = callWithRequestSpy;
const annotationMock: Annotation = {
annotation: 'Annotation text',
@ -149,7 +137,7 @@ describe('annotation_service', () => {
it('should remove ._id and .key before updating annotation', async done => {
const { indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
const mockFunct = callWithRequestSpy;
const annotationMock: Annotation = {
_id: 'mockId',
@ -181,7 +169,7 @@ describe('annotation_service', () => {
it('should update annotation text and the username for modified_username', async done => {
const { getAnnotations, indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
const mockFunct = callWithRequestSpy;
const indexAnnotationArgsMock: IndexAnnotationArgs = {
jobIds: [jobIdMock],

View file

@ -6,7 +6,7 @@
import Boom from 'boom';
import _ from 'lodash';
import { RequestHandlerContext } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { ANNOTATION_TYPE } from '../../../../../legacy/plugins/ml/common/constants/annotations';
import {
@ -68,8 +68,7 @@ export type callWithRequestType = (
params: annotationProviderParams
) => Promise<any>;
export function annotationProvider(context: RequestHandlerContext) {
const callAsCurrentUser = context.ml!.mlClient.callAsCurrentUser;
export function annotationProvider(callAsCurrentUser: APICaller) {
async function indexAnnotation(annotation: Annotation, username: string) {
if (isAnnotation(annotation) === false) {
// No need to translate, this will not be exposed in the UI.

View file

@ -4,11 +4,11 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { annotationProvider } from './annotation';
export function annotationServiceProvider(context: RequestHandlerContext) {
export function annotationServiceProvider(callAsCurrentUser: APICaller) {
return {
...annotationProvider(context),
...annotationProvider(callAsCurrentUser),
};
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { BucketSpanEstimatorData } from '../../../../../legacy/plugins/ml/public/application/services/ml_api_service';
export function estimateBucketSpanFactory(

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
export function calculateModelMemoryLimitProvider(
callAsCurrentUser: APICaller

View file

@ -6,7 +6,7 @@
import { difference } from 'lodash';
import Boom from 'boom';
import { IScopedClusterClient } from 'src/core/server';
import { IScopedClusterClient } from 'kibana/server';
import { EventManager, CalendarEvent } from './event_manager';
interface BasicCalendar {

View file

@ -4,26 +4,18 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'kibana/server';
import { APICaller, SavedObjectsClientContract } from 'kibana/server';
import { Module } from '../../../../../legacy/plugins/ml/common/types/modules';
import { DataRecognizer } from '../data_recognizer';
describe('ML - data recognizer', () => {
const dr = new DataRecognizer(({
ml: {
mlClient: {
callAsCurrentUser: jest.fn(),
},
},
core: {
savedObjects: {
client: {
find: jest.fn(),
bulkCreate: jest.fn(),
},
},
},
} as unknown) as RequestHandlerContext);
const dr = new DataRecognizer(
jest.fn() as APICaller,
({
find: jest.fn(),
bulkCreate: jest.fn(),
} as never) as SavedObjectsClientContract
);
const moduleIds = [
'apache_ecs',

View file

@ -7,7 +7,7 @@
import fs from 'fs';
import Boom from 'boom';
import numeral from '@elastic/numeral';
import { CallAPIOptions, RequestHandlerContext, SavedObjectsClientContract } from 'kibana/server';
import { CallAPIOptions, APICaller, SavedObjectsClientContract } from 'kibana/server';
import { IndexPatternAttributes } from 'src/plugins/data/server';
import { merge } from 'lodash';
import { CombinedJobWithStats } from '../../../../../legacy/plugins/ml/common/types/anomaly_detection_jobs';
@ -68,7 +68,7 @@ interface Config {
json: RawModuleConfig;
}
interface Result {
export interface RecognizeResult {
id: string;
title: string;
query: any;
@ -118,9 +118,9 @@ export class DataRecognizer {
options?: CallAPIOptions
) => Promise<any>;
constructor(context: RequestHandlerContext) {
this.callAsCurrentUser = context.ml!.mlClient.callAsCurrentUser;
this.savedObjectsClient = context.core.savedObjects.client;
constructor(callAsCurrentUser: APICaller, savedObjectsClient: SavedObjectsClientContract) {
this.callAsCurrentUser = callAsCurrentUser;
this.savedObjectsClient = savedObjectsClient;
}
// list all directories under the given directory
@ -189,9 +189,9 @@ export class DataRecognizer {
}
// called externally by an endpoint
async findMatches(indexPattern: string): Promise<Result[]> {
async findMatches(indexPattern: string): Promise<RecognizeResult[]> {
const manifestFiles = await this.loadManifestFiles();
const results: Result[] = [];
const results: RecognizeResult[] = [];
await Promise.all(
manifestFiles.map(async i => {

View file

@ -4,4 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export { DataRecognizer } from './data_recognizer';
export { DataRecognizer, RecognizeResult } from './data_recognizer';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { CallAPIOptions, IScopedClusterClient } from 'src/core/server';
import { CallAPIOptions, IScopedClusterClient } from 'kibana/server';
import _ from 'lodash';
import { ML_JOB_FIELD_TYPES } from '../../../../../legacy/plugins/ml/common/constants/field_types';
import { getSafeAggregationName } from '../../../../../legacy/plugins/ml/common/util/job_utils';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
export function fieldsServiceProvider(
callAsCurrentUser: APICaller

View file

@ -5,7 +5,7 @@
*/
import Boom from 'boom';
import { RequestHandlerContext } from 'kibana/server';
import { APICaller } from 'kibana/server';
import { FindFileStructureResponse } from '../../../../../legacy/plugins/ml/common/types/file_datavisualizer';
export type InputData = any[];
@ -25,12 +25,12 @@ export interface AnalysisResult {
overrides?: FormattedOverrides;
}
export function fileDataVisualizerProvider(context: RequestHandlerContext) {
export function fileDataVisualizerProvider(callAsCurrentUser: APICaller) {
async function analyzeFile(data: any, overrides: any): Promise<AnalysisResult> {
let results = [];
try {
results = await context.ml!.mlClient.callAsCurrentUser('ml.fileStructure', {
results = await callAsCurrentUser('ml.fileStructure', {
body: data,
...overrides,
});

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'kibana/server';
import { APICaller } from 'kibana/server';
import { INDEX_META_DATA_CREATED_BY } from '../../../../../legacy/plugins/ml/common/constants/file_datavisualizer';
import { InputData } from './file_data_visualizer';
@ -30,9 +30,7 @@ interface Failure {
doc: any;
}
export function importDataProvider(context: RequestHandlerContext) {
const callAsCurrentUser = context.ml!.mlClient.callAsCurrentUser;
export function importDataProvider(callAsCurrentUser: APICaller) {
async function importData(
id: string,
index: string,

View file

@ -5,7 +5,7 @@
*/
import Boom from 'boom';
import { IScopedClusterClient } from 'src/core/server';
import { IScopedClusterClient } from 'kibana/server';
import {
DetectorRule,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
export function jobAuditMessagesProvider(
callAsCurrentUser: APICaller

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { i18n } from '@kbn/i18n';
import {
JOB_STATE,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { CalendarManager } from '../calendar';
import { GLOBAL_CALENDAR } from '../../../../../legacy/plugins/ml/common/constants/calendars';
import { Job } from '../../../../../legacy/plugins/ml/common/types/anomaly_detection_jobs';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { datafeedsProvider } from './datafeeds';
import { jobsProvider } from './jobs';
import { groupsProvider } from './groups';

View file

@ -6,7 +6,7 @@
import { i18n } from '@kbn/i18n';
import { uniq } from 'lodash';
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import {
JOB_STATE,
DATAFEED_STATE,

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { SavedObject } from 'src/core/server';
import { SavedObject } from 'kibana/server';
import { IndexPatternAttributes } from 'src/plugins/data/server';
import { SavedObjectsClientContract } from 'kibana/server';
import { FieldId } from '../../../../../../legacy/plugins/ml/common/types/fields';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { TypeOf } from '@kbn/config-schema';
import { validateJobSchema } from '../../routes/schemas/job_validation_schema';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { CombinedJob } from '../../../../../legacy/plugins/ml/common/types/anomaly_detection_jobs';
export function validateCardinality(callAsCurrentUser: APICaller, job: CombinedJob): any[];

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller } from 'src/core/server';
import { APICaller } from 'kibana/server';
import { ES_FIELD_TYPES } from '../../../../../../src/plugins/data/server';
import { parseInterval } from '../../../../../legacy/plugins/ml/common/util/parse_interval';
import { CombinedJob } from '../../../../../legacy/plugins/ml/common/types/anomaly_detection_jobs';

View file

@ -7,7 +7,7 @@
import _ from 'lodash';
import moment from 'moment';
import { SearchResponse } from 'elasticsearch';
import { RequestHandlerContext } from 'kibana/server';
import { APICaller } from 'kibana/server';
import { buildAnomalyTableItems, AnomaliesTableRecord } from './build_anomaly_table_items';
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../legacy/plugins/ml/common/constants/index_patterns';
import { ANOMALIES_TABLE_DEFAULT_QUERY_SIZE } from '../../../../../legacy/plugins/ml/common/constants/search';
@ -30,9 +30,7 @@ interface Influencer {
fieldValue: any;
}
export function resultsServiceProvider(client: RequestHandlerContext | ((...args: any[]) => any)) {
const callAsCurrentUser =
typeof client === 'object' ? client.ml!.mlClient.callAsCurrentUser : client;
export function resultsServiceProvider(callAsCurrentUser: APICaller) {
// Obtains data for the anomalies table, aggregating anomalies by day or hour as requested.
// Return an Object with properties 'anomalies' and 'interval' (interval used to aggregate anomalies,
// one of day, hour or second. Note 'auto' can be provided as the aggregationInterval in the request,

View file

@ -5,11 +5,16 @@
*/
import { i18n } from '@kbn/i18n';
import { CoreSetup, IScopedClusterClient, Logger, PluginInitializerContext } from 'src/core/server';
import {
CoreSetup,
Plugin,
IScopedClusterClient,
Logger,
PluginInitializerContext,
} from 'kibana/server';
import { PluginsSetup, RouteInitialization } from './types';
import { PLUGIN_ID } from '../../../legacy/plugins/ml/common/constants/app';
// @ts-ignore: could not find declaration file for module
import { elasticsearchJsPlugin } from './client/elasticsearch_ml';
import { makeMlUsageCollector } from './lib/ml_telemetry';
import { initMlServerLog } from './client/log';
@ -34,6 +39,7 @@ import { resultsServiceRoutes } from './routes/results_service';
import { systemRoutes } from './routes/system';
import { MlLicense } from '../../../legacy/plugins/ml/common/license';
import { MlServerLicense } from './lib/license';
import { createSharedServices, SharedServices } from './shared_services';
declare module 'kibana/server' {
interface RequestHandlerContext {
@ -43,7 +49,10 @@ declare module 'kibana/server' {
}
}
export class MlServerPlugin {
export type MlSetupContract = SharedServices;
export type MlStartContract = void;
export class MlServerPlugin implements Plugin<MlSetupContract, MlStartContract, PluginsSetup> {
private log: Logger;
private version: string;
private mlLicense: MlServerLicense;
@ -54,7 +63,7 @@ export class MlServerPlugin {
this.mlLicense = new MlServerLicense();
}
public setup(coreSetup: CoreSetup, plugins: PluginsSetup) {
public setup(coreSetup: CoreSetup, plugins: PluginsSetup): MlSetupContract {
plugins.features.registerFeature({
id: PLUGIN_ID,
name: i18n.translate('xpack.ml.featureRegistry.mlFeatureName', {
@ -124,9 +133,11 @@ export class MlServerPlugin {
coreSetup.getStartServices().then(([core]) => {
makeMlUsageCollector(plugins.usageCollection, core.savedObjects);
});
return createSharedServices(this.mlLicense, plugins.spaces, plugins.cloud);
}
public start() {}
public start(): MlStartContract {}
public stop() {
this.mlLicense.unsubscribe();

View file

@ -62,7 +62,9 @@ export function annotationRoutes(
},
mlLicense.fullLicenseAPIGuard(async (context, request, response) => {
try {
const { getAnnotations } = annotationServiceProvider(context);
const { getAnnotations } = annotationServiceProvider(
context.ml!.mlClient.callAsCurrentUser
);
const resp = await getAnnotations(request.body);
return response.ok({
@ -100,7 +102,9 @@ export function annotationRoutes(
throw getAnnotationsFeatureUnavailableErrorMessage();
}
const { indexAnnotation } = annotationServiceProvider(context);
const { indexAnnotation } = annotationServiceProvider(
context.ml!.mlClient.callAsCurrentUser
);
const currentUser =
securityPlugin !== undefined ? securityPlugin.authc.getCurrentUser(request) : {};
@ -143,7 +147,9 @@ export function annotationRoutes(
}
const annotationId = request.params.annotationId;
const { deleteAnnotation } = annotationServiceProvider(context);
const { deleteAnnotation } = annotationServiceProvider(
context.ml!.mlClient.callAsCurrentUser
);
const resp = await deleteAnnotation(annotationId);
return response.ok({

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'src/core/server';
import { RequestHandlerContext } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'src/core/server';
import { RequestHandlerContext } from 'kibana/server';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';
import {

View file

@ -22,7 +22,7 @@ import { RouteInitialization } from '../types';
import { incrementFileDataVisualizerIndexCreationCount } from '../lib/ml_telemetry';
function analyzeFiles(context: RequestHandlerContext, data: InputData, overrides: InputOverrides) {
const { analyzeFile } = fileDataVisualizerProvider(context);
const { analyzeFile } = fileDataVisualizerProvider(context.ml!.mlClient.callAsCurrentUser);
return analyzeFile(data, overrides);
}
@ -35,7 +35,7 @@ function importData(
ingestPipeline: InjectPipeline,
data: InputData
) {
const { importData: importDataFunc } = importDataProvider(context);
const { importData: importDataFunc } = importDataProvider(context.ml!.mlClient.callAsCurrentUser);
return importDataFunc(id, index, settings, mappings, ingestPipeline, data);
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'src/core/server';
import { RequestHandlerContext } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';

View file

@ -6,7 +6,7 @@
import Boom from 'boom';
import { schema } from '@kbn/config-schema';
import { IScopedClusterClient } from 'src/core/server';
import { IScopedClusterClient } from 'kibana/server';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';
import {

View file

@ -5,7 +5,7 @@
*/
import Boom from 'boom';
import { RequestHandlerContext } from 'src/core/server';
import { RequestHandlerContext } from 'kibana/server';
import { schema, TypeOf } from '@kbn/config-schema';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';

View file

@ -5,6 +5,7 @@
*/
import { schema } from '@kbn/config-schema';
import { RequestHandlerContext } from 'kibana/server';
import { DatafeedOverride, JobOverride } from '../../../../legacy/plugins/ml/common/types/modules';
import { wrapError } from '../client/error_wrapper';
@ -13,12 +14,18 @@ import { getModuleIdParamSchema, setupModuleBodySchema } from './schemas/modules
import { RouteInitialization } from '../types';
function recognize(context: RequestHandlerContext, indexPatternTitle: string) {
const dr = new DataRecognizer(context);
const dr = new DataRecognizer(
context.ml!.mlClient.callAsCurrentUser,
context.core.savedObjects.client
);
return dr.findMatches(indexPatternTitle);
}
function getModule(context: RequestHandlerContext, moduleId: string) {
const dr = new DataRecognizer(context);
const dr = new DataRecognizer(
context.ml!.mlClient.callAsCurrentUser,
context.core.savedObjects.client
);
if (moduleId === undefined) {
return dr.listModules();
} else {
@ -40,7 +47,10 @@ function saveModuleItems(
jobOverrides: JobOverride[],
datafeedOverrides: DatafeedOverride[]
) {
const dr = new DataRecognizer(context);
const dr = new DataRecognizer(
context.ml!.mlClient.callAsCurrentUser,
context.core.savedObjects.client
);
return dr.setupModuleItems(
moduleId,
prefix,
@ -57,7 +67,10 @@ function saveModuleItems(
}
function dataRecognizerJobsExist(context: RequestHandlerContext, moduleId: string) {
const dr = new DataRecognizer(context);
const dr = new DataRecognizer(
context.ml!.mlClient.callAsCurrentUser,
context.core.savedObjects.client
);
return dr.dataRecognizerJobsExist(moduleId);
}

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { RequestHandlerContext } from 'src/core/server';
import { RequestHandlerContext } from 'kibana/server';
import { schema } from '@kbn/config-schema';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../types';
@ -18,7 +18,7 @@ import {
import { resultsServiceProvider } from '../models/results_service';
function getAnomaliesTableData(context: RequestHandlerContext, payload: any) {
const rs = resultsServiceProvider(context);
const rs = resultsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const {
jobIds,
criteriaFields,
@ -48,24 +48,24 @@ function getAnomaliesTableData(context: RequestHandlerContext, payload: any) {
}
function getCategoryDefinition(context: RequestHandlerContext, payload: any) {
const rs = resultsServiceProvider(context);
const rs = resultsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
return rs.getCategoryDefinition(payload.jobId, payload.categoryId);
}
function getCategoryExamples(context: RequestHandlerContext, payload: any) {
const rs = resultsServiceProvider(context);
const rs = resultsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobId, categoryIds, maxExamples } = payload;
return rs.getCategoryExamples(jobId, categoryIds, maxExamples);
}
function getMaxAnomalyScore(context: RequestHandlerContext, payload: any) {
const rs = resultsServiceProvider(context);
const rs = resultsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds, earliestMs, latestMs } = payload;
return rs.getMaxAnomalyScore(jobIds, earliestMs, latestMs);
}
function getPartitionFieldsValues(context: RequestHandlerContext, payload: any) {
const rs = resultsServiceProvider(context);
const rs = resultsServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobId, searchTerm, criteriaFields, earliestMs, latestMs } = payload;
return rs.getPartitionFieldsValues(jobId, searchTerm, criteriaFields, earliestMs, latestMs);
}

View file

@ -4,6 +4,4 @@
* you may not use this file except in compliance with the Elastic License.
*/
export interface InjectorService {
get<T>(name: string, caller?: string): T;
}
export { SharedServices, createSharedServices } from './shared_services';

View file

@ -0,0 +1,26 @@
/*
* 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 { MlServerLicense } from '../lib/license';
export type LicenseCheck = () => void;
export function licenseChecks(
mlLicense: MlServerLicense
): { isFullLicense: LicenseCheck; isMinimumLicense: LicenseCheck } {
return {
isFullLicense() {
if (mlLicense.isFullLicense() === false) {
throw Error('Platinum, Enterprise or trial license needed');
}
},
isMinimumLicense() {
if (mlLicense.isMinimumLicense() === false) {
throw Error('Basic license needed');
}
},
};
}

View file

@ -0,0 +1,29 @@
/*
* 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 { APICaller } from 'kibana/server';
import { LicenseCheck } from '../license_checks';
export interface AnomalyDetectorsProvider {
anomalyDetectorsProvider(
callAsCurrentUser: APICaller
): {
jobs(jobId?: string): Promise<any>;
};
}
export function getAnomalyDetectorsProvider(isFullLicense: LicenseCheck): AnomalyDetectorsProvider {
return {
anomalyDetectorsProvider(callAsCurrentUser: APICaller) {
return {
jobs(jobId?: string) {
isFullLicense();
return callAsCurrentUser('ml.jobs', jobId !== undefined ? { jobId } : {});
},
};
},
};
}

View file

@ -0,0 +1,22 @@
/*
* 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 { APICaller } from 'kibana/server';
import { LicenseCheck } from '../license_checks';
import { jobServiceProvider } from '../../models/job_service';
export interface JobServiceProvider {
jobServiceProvider(callAsCurrentUser: APICaller): ReturnType<typeof jobServiceProvider>;
}
export function getJobServiceProvider(isFullLicense: LicenseCheck): JobServiceProvider {
return {
jobServiceProvider(callAsCurrentUser: APICaller) {
isFullLicense();
return jobServiceProvider(callAsCurrentUser);
},
};
}

View file

@ -0,0 +1,95 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License;
* you may not use this file except in compliance with the Elastic License.
*/
import { APICaller, SavedObjectsClientContract } from 'kibana/server';
import { LicenseCheck } from '../license_checks';
import { DataRecognizer, RecognizeResult } from '../../models/data_recognizer';
import {
Module,
DatafeedOverride,
JobOverride,
DataRecognizerConfigResponse,
} from '../../../../../legacy/plugins/ml/common/types/modules';
export interface ModulesProvider {
modulesProvider(
callAsCurrentUser: APICaller,
savedObjectsClient: SavedObjectsClientContract
): {
recognize(indexPatternTitle: string): Promise<RecognizeResult[]>;
getModule(moduleId?: string): Promise<Module | Module[]>;
saveModuleItems(
moduleId: string,
prefix: string,
groups: string[],
indexPatternName: string,
query: any,
useDedicatedIndex: boolean,
startDatafeed: boolean,
start: number,
end: number,
jobOverrides: JobOverride[],
datafeedOverrides: DatafeedOverride[]
): Promise<DataRecognizerConfigResponse>;
};
}
export function getModulesProvider(isFullLicense: LicenseCheck): ModulesProvider {
return {
modulesProvider(callAsCurrentUser: APICaller, savedObjectsClient: SavedObjectsClientContract) {
isFullLicense();
return {
recognize(indexPatternTitle: string) {
const dr = dataRecognizerFactory(callAsCurrentUser, savedObjectsClient);
return dr.findMatches(indexPatternTitle);
},
getModule(moduleId?: string) {
const dr = dataRecognizerFactory(callAsCurrentUser, savedObjectsClient);
if (moduleId === undefined) {
return dr.listModules();
} else {
return dr.getModule(moduleId);
}
},
saveModuleItems(
moduleId: string,
prefix: string,
groups: string[],
indexPatternName: string,
query: any,
useDedicatedIndex: boolean,
startDatafeed: boolean,
start: number,
end: number,
jobOverrides: JobOverride[],
datafeedOverrides: DatafeedOverride[]
) {
const dr = dataRecognizerFactory(callAsCurrentUser, savedObjectsClient);
return dr.setupModuleItems(
moduleId,
prefix,
groups,
indexPatternName,
query,
useDedicatedIndex,
startDatafeed,
start,
end,
jobOverrides,
datafeedOverrides
);
},
};
},
};
}
function dataRecognizerFactory(
callAsCurrentUser: APICaller,
savedObjectsClient: SavedObjectsClientContract
) {
return new DataRecognizer(callAsCurrentUser, savedObjectsClient);
}

View file

@ -0,0 +1,22 @@
/*
* 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 { APICaller } from 'kibana/server';
import { LicenseCheck } from '../license_checks';
import { resultsServiceProvider } from '../../models/results_service';
export interface ResultsServiceProvider {
resultsServiceProvider(callAsCurrentUser: APICaller): ReturnType<typeof resultsServiceProvider>;
}
export function getResultsServiceProvider(isFullLicense: LicenseCheck): ResultsServiceProvider {
return {
resultsServiceProvider(callAsCurrentUser: APICaller) {
isFullLicense();
return resultsServiceProvider(callAsCurrentUser);
},
};
}

View file

@ -0,0 +1,74 @@
/*
* 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 { APICaller } from 'kibana/server';
import { SearchResponse, SearchParams } from 'elasticsearch';
import { MlServerLicense } from '../../lib/license';
import { CloudSetup } from '../../../../cloud/server';
import { LicenseCheck } from '../license_checks';
import { spacesUtilsProvider, RequestFacade } from '../../lib/spaces_utils';
import { SpacesPluginSetup } from '../../../../spaces/server';
import { privilegesProvider, MlCapabilities } from '../../lib/check_privileges';
import { MlInfoResponse } from '../../../../../legacy/plugins/ml/common/types/ml_server_info';
import { ML_RESULTS_INDEX_PATTERN } from '../../../../../legacy/plugins/ml/common/constants/index_patterns';
export interface MlSystemProvider {
mlSystemProvider(
callAsCurrentUser: APICaller,
request: RequestFacade
): {
mlCapabilities(ignoreSpaces?: boolean): Promise<MlCapabilities>;
mlInfo(): Promise<MlInfoResponse>;
mlSearch<T>(searchParams: SearchParams): Promise<SearchResponse<T>>;
};
}
export function getMlSystemProvider(
isMinimumLicense: LicenseCheck,
isFullLicense: LicenseCheck,
mlLicense: MlServerLicense,
spaces: SpacesPluginSetup | undefined,
cloud: CloudSetup | undefined
): MlSystemProvider {
return {
mlSystemProvider(callAsCurrentUser: APICaller, request: RequestFacade) {
return {
mlCapabilities(ignoreSpaces?: boolean) {
isMinimumLicense();
const { isMlEnabledInSpace } =
spaces !== undefined
? spacesUtilsProvider(spaces, request)
: { isMlEnabledInSpace: async () => true };
const { getPrivileges } = privilegesProvider(
callAsCurrentUser,
mlLicense,
isMlEnabledInSpace,
ignoreSpaces
);
return getPrivileges();
},
async mlInfo(): Promise<MlInfoResponse> {
isMinimumLicense();
const info = await callAsCurrentUser('ml.info');
const cloudId = cloud && cloud.cloudId;
return {
...info,
cloudId,
};
},
async mlSearch<T>(searchParams: SearchParams): Promise<SearchResponse<T>> {
isFullLicense();
return callAsCurrentUser('search', {
...searchParams,
index: ML_RESULTS_INDEX_PATTERN,
});
},
};
},
};
}

View file

@ -0,0 +1,41 @@
/*
* 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 { MlServerLicense } from '../lib/license';
import { SpacesPluginSetup } from '../../../spaces/server';
import { CloudSetup } from '../../../cloud/server';
import { licenseChecks } from './license_checks';
import { MlSystemProvider, getMlSystemProvider } from './providers/system';
import { JobServiceProvider, getJobServiceProvider } from './providers/job_service';
import { ModulesProvider, getModulesProvider } from './providers/modules';
import { ResultsServiceProvider, getResultsServiceProvider } from './providers/results_service';
import {
AnomalyDetectorsProvider,
getAnomalyDetectorsProvider,
} from './providers/anomaly_detectors';
export type SharedServices = JobServiceProvider &
AnomalyDetectorsProvider &
MlSystemProvider &
ModulesProvider &
ResultsServiceProvider;
export function createSharedServices(
mlLicense: MlServerLicense,
spaces: SpacesPluginSetup | undefined,
cloud: CloudSetup
): SharedServices {
const { isFullLicense, isMinimumLicense } = licenseChecks(mlLicense);
return {
...getJobServiceProvider(isFullLicense),
...getAnomalyDetectorsProvider(isFullLicense),
...getMlSystemProvider(isMinimumLicense, isFullLicense, mlLicense, spaces, cloud),
...getModulesProvider(isFullLicense),
...getResultsServiceProvider(isFullLicense),
};
}

View file

@ -6,7 +6,7 @@
import { UsageCollectionSetup } from 'src/plugins/usage_collection/server';
import { HomeServerPluginSetup } from 'src/plugins/home/server';
import { IRouter } from 'src/core/server';
import { IRouter } from 'kibana/server';
import { CloudSetup } from '../../cloud/server';
import { SecurityPluginSetup } from '../../security/server';
import { PluginSetupContract as FeaturesPluginSetup } from '../../features/server';