[ML] New Platform server shim: update annotation routes to use new platform router (#57067) (#57361)
* update annotation routes to use NP router * create ts file for feature check * update schema to allow null value for earliest/latestMs * update annotations model test * use NP security plugin to access user info
This commit is contained in:
parent
f5251bbeda
commit
6797a0d27f
|
@ -86,7 +86,7 @@ export const ml = (kibana: any) => {
|
|||
const { usageCollection, cloud, home } = kbnServer.newPlatform.setup.plugins;
|
||||
const plugins = {
|
||||
elasticsearch: server.plugins.elasticsearch, // legacy
|
||||
security: server.plugins.security,
|
||||
security: server.newPlatform.setup.plugins.security,
|
||||
xpackMain: server.plugins.xpack_main,
|
||||
spaces: server.plugins.spaces,
|
||||
home,
|
||||
|
|
11
x-pack/legacy/plugins/ml/server/lib/check_annotations/index.d.ts
vendored
Normal file
11
x-pack/legacy/plugins/ml/server/lib/check_annotations/index.d.ts
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
* 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 { IScopedClusterClient } from 'src/core/server';
|
||||
|
||||
export function isAnnotationsFeatureAvailable(
|
||||
callAsCurrentUser: IScopedClusterClient['callAsCurrentUser']
|
||||
): boolean;
|
|
@ -16,20 +16,20 @@ import {
|
|||
// - ML_ANNOTATIONS_INDEX_PATTERN index is present
|
||||
// - ML_ANNOTATIONS_INDEX_ALIAS_READ alias is present
|
||||
// - ML_ANNOTATIONS_INDEX_ALIAS_WRITE alias is present
|
||||
export async function isAnnotationsFeatureAvailable(callWithRequest) {
|
||||
export async function isAnnotationsFeatureAvailable(callAsCurrentUser) {
|
||||
try {
|
||||
const indexParams = { index: ML_ANNOTATIONS_INDEX_PATTERN };
|
||||
|
||||
const annotationsIndexExists = await callWithRequest('indices.exists', indexParams);
|
||||
const annotationsIndexExists = await callAsCurrentUser('indices.exists', indexParams);
|
||||
if (!annotationsIndexExists) return false;
|
||||
|
||||
const annotationsReadAliasExists = await callWithRequest('indices.existsAlias', {
|
||||
const annotationsReadAliasExists = await callAsCurrentUser('indices.existsAlias', {
|
||||
name: ML_ANNOTATIONS_INDEX_ALIAS_READ,
|
||||
});
|
||||
|
||||
if (!annotationsReadAliasExists) return false;
|
||||
|
||||
const annotationsWriteAliasExists = await callWithRequest('indices.existsAlias', {
|
||||
const annotationsWriteAliasExists = await callAsCurrentUser('indices.existsAlias', {
|
||||
name: ML_ANNOTATIONS_INDEX_ALIAS_WRITE,
|
||||
});
|
||||
if (!annotationsWriteAliasExists) return false;
|
||||
|
|
|
@ -6,6 +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 { ANNOTATION_TYPE } from '../../../common/constants/annotations';
|
||||
import { ML_ANNOTATIONS_INDEX_ALIAS_WRITE } from '../../../common/constants/index_patterns';
|
||||
|
@ -19,23 +20,30 @@ const acknowledgedResponseMock = { acknowledged: true };
|
|||
const jobIdMock = 'jobIdMock';
|
||||
|
||||
describe('annotation_service', () => {
|
||||
let callWithRequestSpy: jest.Mock;
|
||||
let callWithRequestSpy: any;
|
||||
|
||||
beforeEach(() => {
|
||||
callWithRequestSpy = jest.fn((action: string) => {
|
||||
switch (action) {
|
||||
case 'delete':
|
||||
case 'index':
|
||||
return Promise.resolve(acknowledgedResponseMock);
|
||||
case 'search':
|
||||
return Promise.resolve(getAnnotationsResponseMock);
|
||||
}
|
||||
});
|
||||
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;
|
||||
});
|
||||
|
||||
describe('deleteAnnotation()', () => {
|
||||
it('should delete annotation', async done => {
|
||||
const { deleteAnnotation } = annotationServiceProvider(callWithRequestSpy);
|
||||
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
|
||||
|
||||
const annotationMockId = 'mockId';
|
||||
const deleteParamsMock: DeleteParams = {
|
||||
|
@ -46,8 +54,8 @@ describe('annotation_service', () => {
|
|||
|
||||
const response = await deleteAnnotation(annotationMockId);
|
||||
|
||||
expect(callWithRequestSpy.mock.calls[0][0]).toBe('delete');
|
||||
expect(callWithRequestSpy.mock.calls[0][1]).toEqual(deleteParamsMock);
|
||||
expect(mockFunct.mock.calls[0][0]).toBe('delete');
|
||||
expect(mockFunct.mock.calls[0][1]).toEqual(deleteParamsMock);
|
||||
expect(response).toBe(acknowledgedResponseMock);
|
||||
done();
|
||||
});
|
||||
|
@ -56,6 +64,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 indexAnnotationArgsMock: IndexAnnotationArgs = {
|
||||
jobIds: [jobIdMock],
|
||||
|
@ -66,8 +75,8 @@ describe('annotation_service', () => {
|
|||
|
||||
const response: GetResponse = await getAnnotations(indexAnnotationArgsMock);
|
||||
|
||||
expect(callWithRequestSpy.mock.calls[0][0]).toBe('search');
|
||||
expect(callWithRequestSpy.mock.calls[0][1]).toEqual(getAnnotationsRequestMock);
|
||||
expect(mockFunct.mock.calls[0][0]).toBe('search');
|
||||
expect(mockFunct.mock.calls[0][1]).toEqual(getAnnotationsRequestMock);
|
||||
expect(Object.keys(response.annotations)).toHaveLength(1);
|
||||
expect(response.annotations[jobIdMock]).toHaveLength(2);
|
||||
expect(isAnnotations(response.annotations[jobIdMock])).toBeTruthy();
|
||||
|
@ -81,9 +90,15 @@ describe('annotation_service', () => {
|
|||
message: 'mock error message',
|
||||
};
|
||||
|
||||
const callWithRequestSpyError = jest.fn(() => {
|
||||
return Promise.resolve(mockEsError);
|
||||
});
|
||||
const callWithRequestSpyError = ({
|
||||
ml: {
|
||||
mlClient: {
|
||||
callAsCurrentUser: jest.fn(() => {
|
||||
return Promise.resolve(mockEsError);
|
||||
}),
|
||||
},
|
||||
},
|
||||
} as unknown) as RequestHandlerContext;
|
||||
|
||||
const { getAnnotations } = annotationServiceProvider(callWithRequestSpyError);
|
||||
|
||||
|
@ -103,6 +118,7 @@ describe('annotation_service', () => {
|
|||
describe('indexAnnotation()', () => {
|
||||
it('should index annotation', async done => {
|
||||
const { indexAnnotation } = annotationServiceProvider(callWithRequestSpy);
|
||||
const mockFunct = callWithRequestSpy.ml.mlClient.callAsCurrentUser;
|
||||
|
||||
const annotationMock: Annotation = {
|
||||
annotation: 'Annotation text',
|
||||
|
@ -114,10 +130,10 @@ describe('annotation_service', () => {
|
|||
|
||||
const response = await indexAnnotation(annotationMock, usernameMock);
|
||||
|
||||
expect(callWithRequestSpy.mock.calls[0][0]).toBe('index');
|
||||
expect(mockFunct.mock.calls[0][0]).toBe('index');
|
||||
|
||||
// test if the annotation has been correctly augmented
|
||||
const indexParamsCheck = callWithRequestSpy.mock.calls[0][1];
|
||||
const indexParamsCheck = mockFunct.mock.calls[0][1];
|
||||
const annotation = indexParamsCheck.body;
|
||||
expect(annotation.create_username).toBe(usernameMock);
|
||||
expect(annotation.modified_username).toBe(usernameMock);
|
||||
|
@ -130,6 +146,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 annotationMock: Annotation = {
|
||||
_id: 'mockId',
|
||||
|
@ -143,10 +160,10 @@ describe('annotation_service', () => {
|
|||
|
||||
const response = await indexAnnotation(annotationMock, usernameMock);
|
||||
|
||||
expect(callWithRequestSpy.mock.calls[0][0]).toBe('index');
|
||||
expect(mockFunct.mock.calls[0][0]).toBe('index');
|
||||
|
||||
// test if the annotation has been correctly augmented
|
||||
const indexParamsCheck = callWithRequestSpy.mock.calls[0][1];
|
||||
const indexParamsCheck = mockFunct.mock.calls[0][1];
|
||||
const annotation = indexParamsCheck.body;
|
||||
expect(annotation.create_username).toBe(usernameMock);
|
||||
expect(annotation.modified_username).toBe(usernameMock);
|
||||
|
@ -161,6 +178,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 indexAnnotationArgsMock: IndexAnnotationArgs = {
|
||||
jobIds: [jobIdMock],
|
||||
|
@ -184,9 +202,9 @@ describe('annotation_service', () => {
|
|||
|
||||
await indexAnnotation(annotation, modifiedUsernameMock);
|
||||
|
||||
expect(callWithRequestSpy.mock.calls[1][0]).toBe('index');
|
||||
expect(mockFunct.mock.calls[1][0]).toBe('index');
|
||||
// test if the annotation has been correctly updated
|
||||
const indexParamsCheck = callWithRequestSpy.mock.calls[1][1];
|
||||
const indexParamsCheck = mockFunct.mock.calls[1][1];
|
||||
const modifiedAnnotation = indexParamsCheck.body;
|
||||
expect(modifiedAnnotation.annotation).toBe(modifiedAnnotationText);
|
||||
expect(modifiedAnnotation.create_username).toBe(originalUsernameMock);
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
import Boom from 'boom';
|
||||
import _ from 'lodash';
|
||||
import { RequestHandlerContext } from 'src/core/server';
|
||||
|
||||
import { ANNOTATION_TYPE } from '../../../common/constants/annotations';
|
||||
import {
|
||||
|
@ -67,7 +68,8 @@ export type callWithRequestType = (
|
|||
params: annotationProviderParams
|
||||
) => Promise<any>;
|
||||
|
||||
export function annotationProvider(callWithRequest: callWithRequestType) {
|
||||
export function annotationProvider(context: RequestHandlerContext) {
|
||||
const callAsCurrentUser = context.ml!.mlClient.callAsCurrentUser;
|
||||
async function indexAnnotation(annotation: Annotation, username: string) {
|
||||
if (isAnnotation(annotation) === false) {
|
||||
// No need to translate, this will not be exposed in the UI.
|
||||
|
@ -94,7 +96,7 @@ export function annotationProvider(callWithRequest: callWithRequestType) {
|
|||
delete params.body.key;
|
||||
}
|
||||
|
||||
return await callWithRequest('index', params);
|
||||
return await callAsCurrentUser('index', params);
|
||||
}
|
||||
|
||||
async function getAnnotations({
|
||||
|
@ -213,7 +215,7 @@ export function annotationProvider(callWithRequest: callWithRequestType) {
|
|||
};
|
||||
|
||||
try {
|
||||
const resp = await callWithRequest('search', params);
|
||||
const resp = await callAsCurrentUser('search', params);
|
||||
|
||||
if (resp.error !== undefined && resp.message !== undefined) {
|
||||
// No need to translate, this will not be exposed in the UI.
|
||||
|
@ -252,7 +254,7 @@ export function annotationProvider(callWithRequest: callWithRequestType) {
|
|||
refresh: 'wait_for',
|
||||
};
|
||||
|
||||
return await callWithRequest('delete', param);
|
||||
return await callAsCurrentUser('delete', param);
|
||||
}
|
||||
|
||||
return {
|
||||
|
|
|
@ -4,10 +4,11 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import { annotationProvider, callWithRequestType } from './annotation';
|
||||
import { RequestHandlerContext } from 'src/core/server';
|
||||
import { annotationProvider } from './annotation';
|
||||
|
||||
export function annotationServiceProvider(callWithRequest: callWithRequestType) {
|
||||
export function annotationServiceProvider(context: RequestHandlerContext) {
|
||||
return {
|
||||
...annotationProvider(callWithRequest),
|
||||
...annotationProvider(context),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* 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';
|
||||
|
||||
export const indexAnnotationSchema = {
|
||||
timestamp: schema.number(),
|
||||
end_timestamp: schema.number(),
|
||||
annotation: schema.string(),
|
||||
job_id: schema.string(),
|
||||
type: schema.string(),
|
||||
create_time: schema.maybe(schema.number()),
|
||||
create_username: schema.maybe(schema.string()),
|
||||
modified_time: schema.maybe(schema.number()),
|
||||
modified_username: schema.maybe(schema.string()),
|
||||
_id: schema.maybe(schema.string()),
|
||||
key: schema.maybe(schema.string()),
|
||||
};
|
||||
|
||||
export const getAnnotationsSchema = {
|
||||
jobIds: schema.arrayOf(schema.string()),
|
||||
earliestMs: schema.oneOf([schema.nullable(schema.number()), schema.maybe(schema.number())]),
|
||||
latestMs: schema.oneOf([schema.nullable(schema.number()), schema.maybe(schema.number())]),
|
||||
maxAnnotations: schema.number(),
|
||||
};
|
||||
|
||||
export const deleteAnnotationSchema = { annotationId: schema.string() };
|
|
@ -106,6 +106,7 @@ export interface RouteInitialization {
|
|||
xpackMainPlugin: MlXpackMainPlugin;
|
||||
savedObjects?: SavedObjectsLegacyService;
|
||||
spacesPlugin: any;
|
||||
securityPlugin: any;
|
||||
cloud?: CloudSetup;
|
||||
}
|
||||
export interface UsageInitialization {
|
||||
|
@ -212,6 +213,7 @@ export class Plugin {
|
|||
elasticsearchService: core.elasticsearch,
|
||||
xpackMainPlugin: plugins.xpackMain,
|
||||
spacesPlugin: plugins.spaces,
|
||||
securityPlugin: plugins.security,
|
||||
};
|
||||
|
||||
const extendedRouteInitializationDeps: RouteInitialization = {
|
||||
|
|
|
@ -1,78 +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 Boom from 'boom';
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { callWithRequestFactory } from '../client/call_with_request_factory';
|
||||
import { isAnnotationsFeatureAvailable } from '../lib/check_annotations';
|
||||
import { wrapError } from '../client/errors';
|
||||
import { annotationServiceProvider } from '../models/annotation_service';
|
||||
|
||||
import { ANNOTATION_USER_UNKNOWN } from '../../common/constants/annotations';
|
||||
|
||||
function getAnnotationsFeatureUnavailableErrorMessage() {
|
||||
return Boom.badRequest(
|
||||
i18n.translate('xpack.ml.routes.annotations.annotationsFeatureUnavailableErrorMessage', {
|
||||
defaultMessage:
|
||||
'Index and aliases required for the annotations feature have not been' +
|
||||
' created or are not accessible for the current user.',
|
||||
})
|
||||
);
|
||||
}
|
||||
export function annotationRoutes({ commonRouteConfig, elasticsearchPlugin, route }) {
|
||||
route({
|
||||
method: 'POST',
|
||||
path: '/api/ml/annotations',
|
||||
handler(request) {
|
||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
||||
const { getAnnotations } = annotationServiceProvider(callWithRequest);
|
||||
return getAnnotations(request.payload).catch(resp => wrapError(resp));
|
||||
},
|
||||
config: {
|
||||
...commonRouteConfig,
|
||||
},
|
||||
});
|
||||
|
||||
route({
|
||||
method: 'PUT',
|
||||
path: '/api/ml/annotations/index',
|
||||
async handler(request) {
|
||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
||||
const annotationsFeatureAvailable = await isAnnotationsFeatureAvailable(callWithRequest);
|
||||
if (annotationsFeatureAvailable === false) {
|
||||
return getAnnotationsFeatureUnavailableErrorMessage();
|
||||
}
|
||||
|
||||
const { indexAnnotation } = annotationServiceProvider(callWithRequest);
|
||||
const username = _.get(request, 'auth.credentials.username', ANNOTATION_USER_UNKNOWN);
|
||||
return indexAnnotation(request.payload, username).catch(resp => wrapError(resp));
|
||||
},
|
||||
config: {
|
||||
...commonRouteConfig,
|
||||
},
|
||||
});
|
||||
|
||||
route({
|
||||
method: 'DELETE',
|
||||
path: '/api/ml/annotations/delete/{annotationId}',
|
||||
async handler(request) {
|
||||
const callWithRequest = callWithRequestFactory(elasticsearchPlugin, request);
|
||||
const annotationsFeatureAvailable = await isAnnotationsFeatureAvailable(callWithRequest);
|
||||
if (annotationsFeatureAvailable === false) {
|
||||
return getAnnotationsFeatureUnavailableErrorMessage();
|
||||
}
|
||||
|
||||
const annotationId = request.params.annotationId;
|
||||
const { deleteAnnotation } = annotationServiceProvider(callWithRequest);
|
||||
return deleteAnnotation(annotationId).catch(resp => wrapError(resp));
|
||||
},
|
||||
config: {
|
||||
...commonRouteConfig,
|
||||
},
|
||||
});
|
||||
}
|
150
x-pack/legacy/plugins/ml/server/routes/annotations.ts
Normal file
150
x-pack/legacy/plugins/ml/server/routes/annotations.ts
Normal file
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* 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 Boom from 'boom';
|
||||
import _ from 'lodash';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
|
||||
import { schema } from '@kbn/config-schema';
|
||||
import { isAnnotationsFeatureAvailable } from '../lib/check_annotations';
|
||||
import { annotationServiceProvider } from '../models/annotation_service';
|
||||
import { wrapError } from '../client/error_wrapper';
|
||||
import { licensePreRoutingFactory } from '../new_platform/licence_check_pre_routing_factory';
|
||||
import { RouteInitialization } from '../new_platform/plugin';
|
||||
import {
|
||||
deleteAnnotationSchema,
|
||||
getAnnotationsSchema,
|
||||
indexAnnotationSchema,
|
||||
} from '../new_platform/annotations_schema';
|
||||
|
||||
import { ANNOTATION_USER_UNKNOWN } from '../../common/constants/annotations';
|
||||
|
||||
function getAnnotationsFeatureUnavailableErrorMessage() {
|
||||
return Boom.badRequest(
|
||||
i18n.translate('xpack.ml.routes.annotations.annotationsFeatureUnavailableErrorMessage', {
|
||||
defaultMessage:
|
||||
'Index and aliases required for the annotations feature have not been' +
|
||||
' created or are not accessible for the current user.',
|
||||
})
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Routes for annotations
|
||||
*/
|
||||
export function annotationRoutes({ xpackMainPlugin, router, securityPlugin }: RouteInitialization) {
|
||||
/**
|
||||
* @apiGroup Annotations
|
||||
*
|
||||
* @api {post} /api/ml/annotations Gets annotations
|
||||
* @apiName GetAnnotations
|
||||
* @apiDescription Gets annotations.
|
||||
*
|
||||
* @apiParam {String[]} jobIds List of job IDs
|
||||
* @apiParam {String} earliestMs
|
||||
* @apiParam {Number} latestMs
|
||||
* @apiParam {Number} maxAnnotations Max limit of annotations returned
|
||||
*
|
||||
* @apiSuccess {Boolean} success
|
||||
* @apiSuccess {Object} annotations
|
||||
*/
|
||||
router.post(
|
||||
{
|
||||
path: '/api/ml/annotations',
|
||||
validate: {
|
||||
body: schema.object(getAnnotationsSchema),
|
||||
},
|
||||
},
|
||||
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
|
||||
try {
|
||||
const { getAnnotations } = annotationServiceProvider(context);
|
||||
const resp = await getAnnotations(request.body);
|
||||
|
||||
return response.ok({
|
||||
body: resp,
|
||||
});
|
||||
} catch (e) {
|
||||
return response.customError(wrapError(e));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* @apiGroup Annotations
|
||||
*
|
||||
* @api {put} /api/ml/annotations/index Index annotation
|
||||
* @apiName IndexAnnotations
|
||||
* @apiDescription Index the annotation.
|
||||
*
|
||||
* @apiParam {Object} annotation
|
||||
* @apiParam {String} username
|
||||
*/
|
||||
router.put(
|
||||
{
|
||||
path: '/api/ml/annotations/index',
|
||||
validate: {
|
||||
body: schema.object(indexAnnotationSchema),
|
||||
},
|
||||
},
|
||||
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
|
||||
try {
|
||||
const annotationsFeatureAvailable = await isAnnotationsFeatureAvailable(
|
||||
context.ml!.mlClient.callAsCurrentUser
|
||||
);
|
||||
if (annotationsFeatureAvailable === false) {
|
||||
throw getAnnotationsFeatureUnavailableErrorMessage();
|
||||
}
|
||||
|
||||
const { indexAnnotation } = annotationServiceProvider(context);
|
||||
const user = securityPlugin.authc.getCurrentUser(request) || {};
|
||||
const resp = await indexAnnotation(request.body, user.username || ANNOTATION_USER_UNKNOWN);
|
||||
|
||||
return response.ok({
|
||||
body: resp,
|
||||
});
|
||||
} catch (e) {
|
||||
return response.customError(wrapError(e));
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* @apiGroup Annotations
|
||||
*
|
||||
* @api {delete} /api/ml/annotations/index Deletes annotation
|
||||
* @apiName DeleteAnnotation
|
||||
* @apiDescription Deletes specified annotation
|
||||
*
|
||||
* @apiParam {String} annotationId
|
||||
*/
|
||||
router.delete(
|
||||
{
|
||||
path: '/api/ml/annotations/delete/{annotationId}',
|
||||
validate: {
|
||||
params: schema.object(deleteAnnotationSchema),
|
||||
},
|
||||
},
|
||||
licensePreRoutingFactory(xpackMainPlugin, async (context, request, response) => {
|
||||
try {
|
||||
const annotationsFeatureAvailable = await isAnnotationsFeatureAvailable(
|
||||
context.ml!.mlClient.callAsCurrentUser
|
||||
);
|
||||
if (annotationsFeatureAvailable === false) {
|
||||
throw getAnnotationsFeatureUnavailableErrorMessage();
|
||||
}
|
||||
|
||||
const annotationId = request.params.annotationId;
|
||||
const { deleteAnnotation } = annotationServiceProvider(context);
|
||||
const resp = await deleteAnnotation(annotationId);
|
||||
|
||||
return response.ok({
|
||||
body: resp,
|
||||
});
|
||||
} catch (e) {
|
||||
return response.customError(wrapError(e));
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
|
@ -46,6 +46,10 @@
|
|||
"RecognizeIndex",
|
||||
"GetModule",
|
||||
"SetupModule",
|
||||
"CheckExistingModuleJobs"
|
||||
"CheckExistingModuleJobs",
|
||||
"Annotations",
|
||||
"GetAnnotations",
|
||||
"IndexAnnotations",
|
||||
"DeleteAnnotation"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue