[7.x] [ML] New Platform server shim: update job service routes to use new platform router (#57403) (#57595)

* [ML] New Platform server shim: update job service routes to use new platform router (#57403)

* wip: convert jobService route file to TS and use NP router

* add schema definitions for route params

* add api docs description for routes

* update schema and rename client

* update calendarManager

* fix typo in schema

* use NP context savedObjectsClient for rollup true

* request no longer passed to JobServiceProvider

* update anomalyDetectors schema for job update

* add missing key to anomalydetectors schema

* update schema
This commit is contained in:
Melissa Alvarez 2020-02-13 16:33:34 -05:00 committed by GitHub
parent 8d0254c5b0
commit 55f87c0438
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
15 changed files with 783 additions and 384 deletions

View file

@ -21,7 +21,7 @@ export const jobs = {
jobsWithTimerange(dateFormatTz) {
return http({
url: `${basePath()}/jobs/jobs_with_timerange`,
url: `${basePath()}/jobs/jobs_with_time_range`,
method: 'POST',
data: {
dateFormatTz,

View file

@ -6,6 +6,7 @@
import { difference } from 'lodash';
import Boom from 'boom';
import { IScopedClusterClient } from 'src/core/server';
import { EventManager, CalendarEvent } from './event_manager';
interface BasicCalendar {
@ -23,13 +24,12 @@ export interface FormCalendar extends BasicCalendar {
}
export class CalendarManager {
private _client: any;
private _client: IScopedClusterClient['callAsCurrentUser'];
private _eventManager: any;
constructor(isLegacy: boolean, client: any) {
const actualClient = isLegacy === true ? client : client.ml!.mlClient.callAsCurrentUser;
this._client = actualClient;
this._eventManager = new EventManager(actualClient);
constructor(client: any) {
this._client = client;
this._eventManager = new EventManager(client);
}
async getCalendar(calendarId: string) {

View file

@ -7,7 +7,7 @@
import { CalendarManager } from '../calendar';
export function groupsProvider(callWithRequest) {
const calMngr = new CalendarManager(true, callWithRequest);
const calMngr = new CalendarManager(callWithRequest);
async function getAllGroups() {
const groups = {};

View file

@ -14,14 +14,14 @@ import {
topCategoriesProvider,
} from './new_job';
export function jobServiceProvider(callWithRequest, request) {
export function jobServiceProvider(callAsCurrentUser) {
return {
...datafeedsProvider(callWithRequest),
...jobsProvider(callWithRequest),
...groupsProvider(callWithRequest),
...newJobCapsProvider(callWithRequest, request),
...newJobChartsProvider(callWithRequest, request),
...categorizationExamplesProvider(callWithRequest, request),
...topCategoriesProvider(callWithRequest, request),
...datafeedsProvider(callAsCurrentUser),
...jobsProvider(callAsCurrentUser),
...groupsProvider(callAsCurrentUser),
...newJobCapsProvider(callAsCurrentUser),
...newJobChartsProvider(callAsCurrentUser),
...categorizationExamplesProvider(callAsCurrentUser),
...topCategoriesProvider(callAsCurrentUser),
};
}

View file

@ -22,7 +22,7 @@ export function jobsProvider(callWithRequest) {
const { forceDeleteDatafeed, getDatafeedIdsByJobId } = datafeedsProvider(callWithRequest);
const { getAuditMessagesSummary } = jobAuditMessagesProvider(callWithRequest);
const { getLatestBucketTimestampByJob } = resultsServiceProvider(callWithRequest);
const calMngr = new CalendarManager(true, callWithRequest);
const calMngr = new CalendarManager(callWithRequest);
async function forceDeleteJob(jobId) {
return callWithRequest('ml.deleteJob', { jobId, force: true });

View file

@ -5,7 +5,7 @@
*/
import { cloneDeep } from 'lodash';
import { Request } from 'src/legacy/server/kbn_server';
import { SavedObjectsClientContract } from 'kibana/server';
import {
Field,
Aggregation,
@ -40,22 +40,27 @@ export function fieldServiceProvider(
indexPattern: string,
isRollup: boolean,
callWithRequest: any,
request: Request
savedObjectsClient: SavedObjectsClientContract
) {
return new FieldsService(indexPattern, isRollup, callWithRequest, request);
return new FieldsService(indexPattern, isRollup, callWithRequest, savedObjectsClient);
}
class FieldsService {
private _indexPattern: string;
private _isRollup: boolean;
private _callWithRequest: any;
private _request: Request;
private _savedObjectsClient: SavedObjectsClientContract;
constructor(indexPattern: string, isRollup: boolean, callWithRequest: any, request: Request) {
constructor(
indexPattern: string,
isRollup: boolean,
callWithRequest: any,
savedObjectsClient: any
) {
this._indexPattern = indexPattern;
this._isRollup = isRollup;
this._callWithRequest = callWithRequest;
this._request = request;
this._savedObjectsClient = savedObjectsClient;
}
private async loadFieldCaps(): Promise<any> {
@ -104,7 +109,7 @@ class FieldsService {
const rollupService = await rollupServiceProvider(
this._indexPattern,
this._callWithRequest,
this._request
this._savedObjectsClient
);
const rollupConfigs: RollupJob[] | null = await rollupService.getRollupJobs();

View file

@ -18,7 +18,7 @@ import cloudwatchJobCaps from './__mocks__/results/cloudwatch_rollup_job_caps.js
describe('job_service - job_caps', () => {
let callWithRequestNonRollupMock: jest.Mock;
let callWithRequestRollupMock: jest.Mock;
let requestMock: any;
let savedObjectsClientMock: any;
beforeEach(() => {
callWithRequestNonRollupMock = jest.fn((action: string) => {
@ -37,14 +37,10 @@ describe('job_service - job_caps', () => {
}
});
requestMock = {
getSavedObjectsClient: jest.fn(() => {
return {
async find() {
return Promise.resolve(kibanaSavedObjects);
},
};
}),
savedObjectsClientMock = {
async find() {
return Promise.resolve(kibanaSavedObjects);
},
};
});
@ -52,8 +48,8 @@ describe('job_service - job_caps', () => {
it('can get job caps for index pattern', async done => {
const indexPattern = 'farequote-*';
const isRollup = false;
const { newJobCaps } = newJobCapsProvider(callWithRequestNonRollupMock, requestMock);
const response = await newJobCaps(indexPattern, isRollup);
const { newJobCaps } = newJobCapsProvider(callWithRequestNonRollupMock);
const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock);
expect(response).toEqual(farequoteJobCaps);
done();
});
@ -61,8 +57,8 @@ describe('job_service - job_caps', () => {
it('can get rollup job caps for non rollup index pattern', async done => {
const indexPattern = 'farequote-*';
const isRollup = true;
const { newJobCaps } = newJobCapsProvider(callWithRequestNonRollupMock, requestMock);
const response = await newJobCaps(indexPattern, isRollup);
const { newJobCaps } = newJobCapsProvider(callWithRequestNonRollupMock);
const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock);
expect(response).toEqual(farequoteJobCapsEmpty);
done();
});
@ -72,8 +68,8 @@ describe('job_service - job_caps', () => {
it('can get rollup job caps for rollup index pattern', async done => {
const indexPattern = 'cloud_roll_index';
const isRollup = true;
const { newJobCaps } = newJobCapsProvider(callWithRequestRollupMock, requestMock);
const response = await newJobCaps(indexPattern, isRollup);
const { newJobCaps } = newJobCapsProvider(callWithRequestRollupMock);
const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock);
expect(response).toEqual(cloudwatchJobCaps);
done();
});
@ -81,8 +77,8 @@ describe('job_service - job_caps', () => {
it('can get non rollup job caps for rollup index pattern', async done => {
const indexPattern = 'cloud_roll_index';
const isRollup = false;
const { newJobCaps } = newJobCapsProvider(callWithRequestRollupMock, requestMock);
const response = await newJobCaps(indexPattern, isRollup);
const { newJobCaps } = newJobCapsProvider(callWithRequestRollupMock);
const response = await newJobCaps(indexPattern, isRollup, savedObjectsClientMock);
expect(response).not.toEqual(cloudwatchJobCaps);
done();
});

View file

@ -4,7 +4,7 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Request } from 'src/legacy/server/kbn_server';
import { SavedObjectsClientContract } from 'kibana/server';
import { Aggregation, Field, NewJobCaps } from '../../../../common/types/fields';
import { fieldServiceProvider } from './field_service';
@ -12,12 +12,18 @@ interface NewJobCapsResponse {
[indexPattern: string]: NewJobCaps;
}
export function newJobCapsProvider(callWithRequest: any, request: Request) {
export function newJobCapsProvider(callWithRequest: any) {
async function newJobCaps(
indexPattern: string,
isRollup: boolean = false
isRollup: boolean = false,
savedObjectsClient: SavedObjectsClientContract
): Promise<NewJobCapsResponse> {
const fieldService = fieldServiceProvider(indexPattern, isRollup, callWithRequest, request);
const fieldService = fieldServiceProvider(
indexPattern,
isRollup,
callWithRequest,
savedObjectsClient
);
const { aggs, fields } = await fieldService.getData();
convertForStringify(aggs, fields);

View file

@ -4,8 +4,8 @@
* you may not use this file except in compliance with the Elastic License.
*/
import { Request } from 'src/legacy/server/kbn_server';
import { SavedObject } from 'src/core/server';
import { SavedObjectsClientContract } from 'kibana/server';
import { FieldId } from '../../../../common/types/fields';
import { ES_AGGREGATION } from '../../../../common/constants/aggregation_types';
@ -21,9 +21,9 @@ export interface RollupJob {
export async function rollupServiceProvider(
indexPattern: string,
callWithRequest: any,
request: Request
savedObjectsClient: SavedObjectsClientContract
) {
const rollupIndexPatternObject = await loadRollupIndexPattern(indexPattern, request);
const rollupIndexPatternObject = await loadRollupIndexPattern(indexPattern, savedObjectsClient);
let jobIndexPatterns: string[] = [indexPattern];
async function getRollupJobs(): Promise<RollupJob[] | null> {
@ -57,9 +57,8 @@ export async function rollupServiceProvider(
async function loadRollupIndexPattern(
indexPattern: string,
request: Request
savedObjectsClient: SavedObjectsClientContract
): Promise<SavedObject | null> {
const savedObjectsClient = request.getSavedObjectsClient();
const resp = await savedObjectsClient.find({
type: 'index-pattern',
fields: ['title', 'type', 'typeMeta'],

View file

@ -6,6 +6,18 @@
import { schema } from '@kbn/config-schema';
const customRulesSchema = schema.maybe(
schema.arrayOf(
schema.maybe(
schema.object({
actions: schema.arrayOf(schema.string()),
conditions: schema.arrayOf(schema.any()),
scope: schema.maybe(schema.any()),
})
)
)
);
const detectorSchema = schema.object({
identifier: schema.maybe(schema.string()),
function: schema.string(),
@ -14,6 +26,7 @@ const detectorSchema = schema.object({
over_field_name: schema.maybe(schema.string()),
partition_field_name: schema.maybe(schema.string()),
detector_description: schema.maybe(schema.string()),
custom_rules: customRulesSchema,
});
const customUrlSchema = {
@ -34,15 +47,8 @@ export const anomalyDetectionUpdateJobSchema = {
schema.maybe(
schema.object({
detector_index: schema.number(),
custom_rules: schema.arrayOf(
schema.maybe(
schema.object({
actions: schema.arrayOf(schema.string()),
conditions: schema.arrayOf(schema.any()),
scope: schema.maybe(schema.any()),
})
)
),
description: schema.maybe(schema.string()),
custom_rules: customRulesSchema,
})
)
)

View file

@ -0,0 +1,77 @@
/*
* 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 { schema } from '@kbn/config-schema';
const analyzerSchema = {
tokenizer: schema.string(),
filter: schema.maybe(
schema.arrayOf(
schema.object({
type: schema.string(),
stopwords: schema.arrayOf(schema.maybe(schema.string())),
})
)
),
};
export const categorizationFieldExamplesSchema = {
indexPatternTitle: schema.string(),
query: schema.any(),
size: schema.number(),
field: schema.string(),
timeField: schema.maybe(schema.string()),
start: schema.number(),
end: schema.number(),
analyzer: schema.object(analyzerSchema),
};
export const chartSchema = {
indexPatternTitle: schema.string(),
timeField: schema.maybe(schema.string()),
start: schema.maybe(schema.number()),
end: schema.maybe(schema.number()),
intervalMs: schema.number(),
query: schema.any(),
aggFieldNamePairs: schema.arrayOf(schema.any()),
splitFieldName: schema.maybe(schema.nullable(schema.string())),
splitFieldValue: schema.maybe(schema.nullable(schema.string())),
};
export const datafeedIdsSchema = { datafeedIds: schema.arrayOf(schema.maybe(schema.string())) };
export const forceStartDatafeedSchema = {
datafeedIds: schema.arrayOf(schema.maybe(schema.string())),
start: schema.maybe(schema.number()),
end: schema.maybe(schema.number()),
};
export const jobIdsSchema = {
jobIds: schema.maybe(
schema.oneOf([schema.string(), schema.arrayOf(schema.maybe(schema.string()))])
),
};
export const jobsWithTimerangeSchema = { dateFormatTz: schema.maybe(schema.string()) };
export const lookBackProgressSchema = {
jobId: schema.string(),
start: schema.maybe(schema.number()),
end: schema.maybe(schema.number()),
};
export const topCategoriesSchema = { jobId: schema.string(), count: schema.number() };
export const updateGroupsSchema = {
jobs: schema.maybe(
schema.arrayOf(
schema.object({
job_id: schema.maybe(schema.string()),
groups: schema.arrayOf(schema.maybe(schema.string())),
})
)
),
};

View file

@ -50,6 +50,25 @@
"Annotations",
"GetAnnotations",
"IndexAnnotations",
"DeleteAnnotation"
"DeleteAnnotation",
"JobService",
"ForceStartDatafeeds",
"StopDatafeeds",
"DeleteJobs",
"CloseJobs",
"JobsSummary",
"JobsWithTimerange",
"CreateFullJobsList",
"GetAllGroups",
"UpdateGroups",
"DeletingJobTasks",
"JobsExist",
"NewJobCaps",
"NewJobLineChart",
"NewJobPopulationChart",
"GetAllJobAndGroupIds",
"GetLookBackProgress",
"ValidateCategoryExamples",
"TopCategories"
]
}

View file

@ -13,32 +13,32 @@ import { calendarSchema } from '../new_platform/calendars_schema';
import { CalendarManager, Calendar, FormCalendar } from '../models/calendar';
function getAllCalendars(context: RequestHandlerContext) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.getAllCalendars();
}
function getCalendar(context: RequestHandlerContext, calendarId: string) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.getCalendar(calendarId);
}
function newCalendar(context: RequestHandlerContext, calendar: FormCalendar) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.newCalendar(calendar);
}
function updateCalendar(context: RequestHandlerContext, calendarId: string, calendar: Calendar) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.updateCalendar(calendarId, calendar);
}
function deleteCalendar(context: RequestHandlerContext, calendarId: string) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.deleteCalendar(calendarId);
}
function getCalendarsByIds(context: RequestHandlerContext, calendarIds: string) {
const cal = new CalendarManager(false, context);
const cal = new CalendarManager(context.ml!.mlClient.callAsCurrentUser);
return cal.getCalendarsByIds(calendarIds);
}

View file

@ -1,319 +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 { callWithRequestFactory } from '../client/call_with_request_factory';
import { wrapError } from '../client/errors';
import { jobServiceProvider } from '../models/job_service';
export function jobServiceRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
route({
method: 'POST',
path: '/api/ml/jobs/force_start_datafeeds',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { forceStartDatafeeds } = jobServiceProvider(callWithRequest);
const { datafeedIds, start, end } = request.payload;
return forceStartDatafeeds(datafeedIds, start, end).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/stop_datafeeds',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { stopDatafeeds } = jobServiceProvider(callWithRequest);
const { datafeedIds } = request.payload;
return stopDatafeeds(datafeedIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/delete_jobs',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { deleteJobs } = jobServiceProvider(callWithRequest);
const { jobIds } = request.payload;
return deleteJobs(jobIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/close_jobs',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { closeJobs } = jobServiceProvider(callWithRequest);
const { jobIds } = request.payload;
return closeJobs(jobIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/jobs_summary',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { jobsSummary } = jobServiceProvider(callWithRequest);
const { jobIds } = request.payload;
return jobsSummary(jobIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/jobs_with_timerange',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { jobsWithTimerange } = jobServiceProvider(callWithRequest);
const { dateFormatTz } = request.payload;
return jobsWithTimerange(dateFormatTz).catch(resp => {
wrapError(resp);
});
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/jobs',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { createFullJobsList } = jobServiceProvider(callWithRequest);
const { jobIds } = request.payload;
return createFullJobsList(jobIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'GET',
path: '/api/ml/jobs/groups',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { getAllGroups } = jobServiceProvider(callWithRequest);
return getAllGroups().catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/update_groups',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { updateGroups } = jobServiceProvider(callWithRequest);
const { jobs } = request.payload;
return updateGroups(jobs).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'GET',
path: '/api/ml/jobs/deleting_jobs_tasks',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { deletingJobTasks } = jobServiceProvider(callWithRequest);
return deletingJobTasks().catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/jobs_exist',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { jobsExist } = jobServiceProvider(callWithRequest);
const { jobIds } = request.payload;
return jobsExist(jobIds).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'GET',
path: '/api/ml/jobs/new_job_caps/{indexPattern}',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { indexPattern } = request.params;
const isRollup = request.query.rollup === 'true';
const { newJobCaps } = jobServiceProvider(callWithRequest, request);
return newJobCaps(indexPattern, isRollup).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/new_job_line_chart',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const {
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
splitFieldValue,
} = request.payload;
const { newJobLineChart } = jobServiceProvider(callWithRequest, request);
return newJobLineChart(
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
splitFieldValue
).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/new_job_population_chart',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const {
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
} = request.payload;
const { newJobPopulationChart } = jobServiceProvider(callWithRequest, request);
return newJobPopulationChart(
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName
).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'GET',
path: '/api/ml/jobs/all_jobs_and_group_ids',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { getAllJobAndGroupIds } = jobServiceProvider(callWithRequest);
return getAllJobAndGroupIds().catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/look_back_progress',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { getLookBackProgress } = jobServiceProvider(callWithRequest);
const { jobId, start, end } = request.payload;
return getLookBackProgress(jobId, start, end).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/categorization_field_examples',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { validateCategoryExamples } = jobServiceProvider(callWithRequest);
const {
indexPatternTitle,
timeField,
query,
size,
field,
start,
end,
analyzer,
} = request.payload;
return validateCategoryExamples(
indexPatternTitle,
query,
size,
field,
timeField,
start,
end,
analyzer
).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
route({
method: 'POST',
path: '/api/ml/jobs/top_categories',
handler(request) {
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
const { topCategories } = jobServiceProvider(callWithRequest);
const { jobId, count } = request.payload;
return topCategories(jobId, count).catch(resp => wrapError(resp));
},
config: {
...commonRouteConfig,
},
});
}

View file

@ -0,0 +1,610 @@
/*
* 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 { schema } from '@kbn/config-schema';
import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
import { wrapError } from '../client/error_wrapper';
import { RouteInitialization } from '../new_platform/plugin';
import {
categorizationFieldExamplesSchema,
chartSchema,
datafeedIdsSchema,
forceStartDatafeedSchema,
jobIdsSchema,
jobsWithTimerangeSchema,
lookBackProgressSchema,
topCategoriesSchema,
updateGroupsSchema,
} from '../new_platform/job_service_schema';
// @ts-ignore no declaration module
import { jobServiceProvider } from '../models/job_service';
/**
* Routes for job service
*/
export function jobServiceRoutes({ xpackMainPlugin, router }: RouteInitialization) {
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/force_start_datafeeds
* @apiName ForceStartDatafeeds
* @apiDescription Starts one or more datafeeds
*/
router.post(
{
path: '/api/ml/jobs/force_start_datafeeds',
validate: {
body: schema.object(forceStartDatafeedSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { forceStartDatafeeds } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { datafeedIds, start, end } = request.body;
const resp = await forceStartDatafeeds(datafeedIds, start, end);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/stop_datafeeds
* @apiName StopDatafeeds
* @apiDescription Stops one or more datafeeds
*/
router.post(
{
path: '/api/ml/jobs/stop_datafeeds',
validate: {
body: schema.object(datafeedIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { stopDatafeeds } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { datafeedIds } = request.body;
const resp = await stopDatafeeds(datafeedIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/delete_jobs
* @apiName DeleteJobs
* @apiDescription Deletes an existing anomaly detection job
*/
router.post(
{
path: '/api/ml/jobs/delete_jobs',
validate: {
body: schema.object(jobIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { deleteJobs } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds } = request.body;
const resp = await deleteJobs(jobIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/close_jobs
* @apiName CloseJobs
* @apiDescription Closes one or more anomaly detection jobs
*/
router.post(
{
path: '/api/ml/jobs/close_jobs',
validate: {
body: schema.object(jobIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { closeJobs } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds } = request.body;
const resp = await closeJobs(jobIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/jobs_summary
* @apiName JobsSummary
* @apiDescription Creates a summary jobs list. Jobs include job stats, datafeed stats, and calendars.
*/
router.post(
{
path: '/api/ml/jobs/jobs_summary',
validate: {
body: schema.object(jobIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { jobsSummary } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds } = request.body;
const resp = await jobsSummary(jobIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/jobs_with_time_range
* @apiName JobsWithTimerange
* @apiDescription Creates a list of jobs with data about the job's timerange
*/
router.post(
{
path: '/api/ml/jobs/jobs_with_time_range',
validate: {
body: schema.object(jobsWithTimerangeSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { jobsWithTimerange } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { dateFormatTz } = request.body;
const resp = await jobsWithTimerange(dateFormatTz);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/jobs
* @apiName CreateFullJobsList
* @apiDescription Creates a list of jobs
*/
router.post(
{
path: '/api/ml/jobs/jobs',
validate: {
body: schema.object(jobIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { createFullJobsList } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds } = request.body;
const resp = await createFullJobsList(jobIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {get} /api/ml/jobs/groups
* @apiName GetAllGroups
* @apiDescription Returns array of group objects with job ids listed for each group
*/
router.get(
{
path: '/api/ml/jobs/groups',
validate: false,
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { getAllGroups } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const resp = await getAllGroups();
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/update_groups
* @apiName UpdateGroups
* @apiDescription Updates 'groups' property of an anomaly detection job
*/
router.post(
{
path: '/api/ml/jobs/update_groups',
validate: {
body: schema.object(updateGroupsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { updateGroups } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobs } = request.body;
const resp = await updateGroups(jobs);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {get} /api/ml/jobs/deleting_jobs_tasks
* @apiName DeletingJobTasks
* @apiDescription Gets the ids of deleting anomaly detection jobs
*/
router.get(
{
path: '/api/ml/jobs/deleting_jobs_tasks',
validate: false,
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { deletingJobTasks } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const resp = await deletingJobTasks();
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/jobs_exist
* @apiName JobsExist
* @apiDescription Checks if each of the jobs in the specified list of IDs exist
*/
router.post(
{
path: '/api/ml/jobs/jobs_exist',
validate: {
body: schema.object(jobIdsSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { jobsExist } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobIds } = request.body;
const resp = await jobsExist(jobIds);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {get} /api/ml/jobs/new_job_caps/:indexPattern
* @apiName NewJobCaps
* @apiDescription Retrieve the capabilities of fields for indices
*/
router.get(
{
path: '/api/ml/jobs/new_job_caps/{indexPattern}',
validate: {
params: schema.object({ indexPattern: schema.string() }),
query: schema.maybe(schema.object({ rollup: schema.maybe(schema.string()) })),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { indexPattern } = request.params;
const isRollup = request.query.rollup === 'true';
const savedObjectsClient = context.core.savedObjects.client;
const { newJobCaps } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const resp = await newJobCaps(indexPattern, isRollup, savedObjectsClient);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/new_job_line_chart
* @apiName NewJobLineChart
* @apiDescription Returns line chart data for anomaly detection job
*/
router.post(
{
path: '/api/ml/jobs/new_job_line_chart',
validate: {
body: schema.object(chartSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const {
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
splitFieldValue,
} = request.body;
const { newJobLineChart } = jobServiceProvider(
context.ml!.mlClient.callAsCurrentUser,
request
);
const resp = await newJobLineChart(
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
splitFieldValue
);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/new_job_population_chart
* @apiName NewJobPopulationChart
* @apiDescription Returns population job chart data
*/
router.post(
{
path: '/api/ml/jobs/new_job_population_chart',
validate: {
body: schema.object(chartSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const {
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName,
} = request.body;
const { newJobPopulationChart } = jobServiceProvider(
context.ml!.mlClient.callAsCurrentUser
);
const resp = await newJobPopulationChart(
indexPatternTitle,
timeField,
start,
end,
intervalMs,
query,
aggFieldNamePairs,
splitFieldName
);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {get} /api/ml/jobs/all_jobs_and_group_ids
* @apiName GetAllJobAndGroupIds
* @apiDescription Returns a list of all job IDs and all group IDs
*/
router.get(
{
path: '/api/ml/jobs/all_jobs_and_group_ids',
validate: false,
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { getAllJobAndGroupIds } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const resp = await getAllJobAndGroupIds();
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/look_back_progress
* @apiName GetLookBackProgress
* @apiDescription Returns current progress of anomaly detection job
*/
router.post(
{
path: '/api/ml/jobs/look_back_progress',
validate: {
body: schema.object(lookBackProgressSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { getLookBackProgress } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobId, start, end } = request.body;
const resp = await getLookBackProgress(jobId, start, end);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/categorization_field_examples
* @apiName ValidateCategoryExamples
* @apiDescription Validates category examples
*/
router.post(
{
path: '/api/ml/jobs/categorization_field_examples',
validate: {
body: schema.object(categorizationFieldExamplesSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { validateCategoryExamples } = jobServiceProvider(
context.ml!.mlClient.callAsCurrentUser
);
const {
indexPatternTitle,
timeField,
query,
size,
field,
start,
end,
analyzer,
} = request.body;
const resp = await validateCategoryExamples(
indexPatternTitle,
query,
size,
field,
timeField,
start,
end,
analyzer
);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
/**
* @apiGroup JobService
*
* @api {post} /api/ml/jobs/top_categories
* @apiName TopCategories
* @apiDescription Returns list of top categories
*/
router.post(
{
path: '/api/ml/jobs/top_categories',
validate: {
body: schema.object(topCategoriesSchema),
},
},
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
try {
const { topCategories } = jobServiceProvider(context.ml!.mlClient.callAsCurrentUser);
const { jobId, count } = request.body;
const resp = await topCategories(jobId, count);
return response.ok({
body: resp,
});
} catch (e) {
return response.customError(wrapError(e));
}
})
);
}