import type { IScopedClusterClient } from 'kibana/server' ; import { wrapError } from '../client/error_wrapper' ; import { analyticsAuditMessagesProvider } from '../models/data_frame_analytics/analytics_audit_messages' ; import type { RouteInitialization } from '../types' ; import { JOB_MAP_NODE_TYPES } from '../../common/constants/data_frame_analytics' ; import type { Field , Aggregation } from '../../common/types/fields' ; import { dataAnalyticsJobConfigSchema , dataAnalyticsJobUpdateSchema , dataAnalyticsEvaluateSchema , dataAnalyticsExplainSchema , analyticsIdSchema , analyticsMapQuerySchema , stopsDataFrameAnalyticsJobQuerySchema , deleteDataFrameAnalyticsJobSchema , jobsExistSchema , analyticsQuerySchema , analyticsNewJobCapsParamsSchema , analyticsNewJobCapsQuerySchema , } from './schemas/data_analytics_schema' ; import type { GetAnalyticsMapArgs , ExtendAnalyticsMapArgs , } from '../models/data_frame_analytics/types' ; import { DataViewHandler } from '../models/data_frame_analytics/index_patterns' ; import { AnalyticsManager } from '../models/data_frame_analytics/analytics_manager' ; import { validateAnalyticsJob } from '../models/data_frame_analytics/validation' ; import { fieldServiceProvider } from '../models/job_service/new_job_caps/field_service' ; import type { DeleteDataFrameAnalyticsWithIndexStatus } from '../../common/types/data_frame_analytics' ; import { getAuthorizationHeader } from '../lib/request_authorization' ; import type { MlClient } from '../lib/ml_client' ; import type { DataViewsService } from '../../../../../src/plugins/data_views/common' ; function getDataViewId ( dataViewsService , patternName ) { const iph = new DataViewHandler ( dataViewsService ) ; return iph . getDataViewId ( patternName ) ; } function deleteDestDataViewById ( dataViewsService , dataViewId ) { const iph = new DataViewHandler ( dataViewsService ) ; return iph . deleteDataViewById ( dataViewId ) ; } function getAnalyticsMap ( mlClient , client , idOptions ) { const analytics = new AnalyticsManager ( mlClient , client ) ; return analytics . getAnalyticsMap ( idOptions ) ; } function getExtendedMap ( mlClient , client , idOptions ) { const analytics = new AnalyticsManager ( mlClient , client ) ; return analytics . extendAnalyticsMapForAnalyticsJob ( idOptions ) ; } function convertForStringify ( aggs , fields ) { fields . forEach ( ( f ) => { f . aggIds = f . aggs ? f . aggs . map ( ( a ) => a . id ) : [ ] ; delete f . aggs ; } ) ; aggs . forEach ( ( a ) => { if ( a . fields !== undefined ) { a . fieldIds = a . fields . map ( ( f ) => f . id ) ; } delete a . fields ; } ) ; } /**
 * Routes for the data frame analytics
 */ export function dataFrameAnalyticsRoutes ( { router , mlLicense , routeGuard } ) { async function userCanDeleteIndex ( client , destinationIndex ) { if ( ! mlLicense . isSecurityEnabled ( ) ) { return true ; } const { body } = await client . asCurrentUser . security . hasPrivileges ( { body : { index : [ { names : [ destinationIndex ] , privileges : [ 'delete_index' ] , } , ] , } , } ) ; return body ?. has_all_requested === true ; } apiGroup api apiName apiDescription apiSuccess apiSuccess router . get ( { path : '/api/ml/data_frame/analytics' , validate : false , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , response } ) => { try { const { body } = await mlClient . getDataFrameAnalytics ( { size : 1000 , } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . get ( { path : '/api/ml/data_frame/analytics/{analyticsId}' , validate : { params : analyticsIdSchema , query : analyticsQuerySchema , } , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { analyticsId } = request . params ; const { excludeGenerated } = request . query ; const { body } = await mlClient . getDataFrameAnalytics ( { id : analyticsId , ... ( excludeGenerated ? { exclude_generated : true } : { } ) , } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription router . get ( { path : '/api/ml/data_frame/analytics/_stats' , validate : false , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , response } ) => { try { const { body } = await mlClient . getDataFrameAnalyticsStats ( { size : 1000 } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . get ( { path : '/api/ml/data_frame/analytics/{analyticsId}/_stats' , validate : { params : analyticsIdSchema , } , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { analyticsId } = request . params ; const { body } = await mlClient . getDataFrameAnalyticsStats ( { id : analyticsId , } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema apiSchema router . put ( { path : '/api/ml/data_frame/analytics/{analyticsId}' , validate : { params : analyticsIdSchema , body : dataAnalyticsJobConfigSchema , } , options : { tags : [ 'access:ml:canCreateDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { analyticsId } = request . params ; const { body } = await mlClient . putDataFrameAnalytics ( { id : analyticsId , body : request . body , } , getAuthorizationHeader ( request ) ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/_evaluate' , validate : { body : dataAnalyticsEvaluateSchema , } , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { body } = await mlClient . evaluateDataFrame ( { body : request . body , } , getAuthorizationHeader ( request ) ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/analytics/_explain' , validate : { body : dataAnalyticsExplainSchema , } , options : { tags : [ 'access:ml:canCreateDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { body } = await mlClient . explainDataFrameAnalytics ( { body : request . body , } , getAuthorizationHeader ( request ) ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . delete ( { path : '/api/ml/data_frame/analytics/{analyticsId}' , validate : { params : analyticsIdSchema , query : deleteDataFrameAnalyticsJobSchema , } , options : { tags : [ 'access:ml:canDeleteDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , client , request , response , getDataViewsService } ) => { try { const { analyticsId } = request . params ; const { deleteDestIndex , deleteDestIndexPattern } = request . query ; let destinationIndex ; const analyticsJobDeleted = { success : false } ; const destIndexDeleted = { success : false } ; const destIndexPatternDeleted = { success : false , } ; try { const { body } = await mlClient . getDataFrameAnalytics ( { id : analyticsId , } ) ; if ( Array . isArray ( body . data_frame_analytics ) && body . data_frame_analytics . length > 0 ) { destinationIndex = body . data_frame_analytics [ 0 ] . dest . index ; } } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } if ( deleteDestIndex || deleteDestIndexPattern ) { if ( destinationIndex && deleteDestIndex ) { const userCanDeleteDestIndex = await userCanDeleteIndex ( client , destinationIndex ) ; if ( userCanDeleteDestIndex ) { try { await client . asCurrentUser . indices . delete ( { index : destinationIndex , } ) ; destIndexDeleted . success = true ; } catch ( { body } ) { destIndexDeleted . error = body ; } } else { return response . forbidden ( ) ; } } if ( destinationIndex && deleteDestIndexPattern ) { try { const dataViewsService = await getDataViewsService ( ) ; const dataViewId = await getDataViewId ( dataViewsService , destinationIndex ) ; if ( dataViewId ) { await deleteDestDataViewById ( dataViewsService , dataViewId ) ; } destIndexPatternDeleted . success = true ; } catch ( deleteDestIndexPatternError ) { destIndexPatternDeleted . error = deleteDestIndexPatternError ; } } } try { await mlClient . deleteDataFrameAnalytics ( { id : analyticsId , } ) ; analyticsJobDeleted . success = true ; } catch ( { body } ) { analyticsJobDeleted . error = body ; } const results = { analyticsJobDeleted , destIndexDeleted , destIndexPatternDeleted , } ; return response . ok ( { body : results , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/analytics/{analyticsId}/_start' , validate : { params : analyticsIdSchema , } , options : { tags : [ 'access:ml:canStartStopDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { analyticsId } = request . params ; const { body } = await mlClient . startDataFrameAnalytics ( { id : analyticsId , } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema apiSchema router . post ( { path : '/api/ml/data_frame/analytics/{analyticsId}/_stop' , validate : { params : analyticsIdSchema , query : stopsDataFrameAnalyticsJobQuerySchema , } , options : { tags : [ 'access:ml:canStartStopDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { body } = await mlClient . stopDataFrameAnalytics ( { id : request . params . analyticsId , force : request . query . force , } ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/analytics/{analyticsId}/_update' , validate : { params : analyticsIdSchema , body : dataAnalyticsJobUpdateSchema , } , options : { tags : [ 'access:ml:canCreateDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , request , response } ) => { try { const { analyticsId } = request . params ; const { body } = await mlClient . updateDataFrameAnalytics ( { id : analyticsId , body : request . body , } , getAuthorizationHeader ( request ) ) ; return response . ok ( { body , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . get ( { path : '/api/ml/data_frame/analytics/{analyticsId}/messages' , validate : { params : analyticsIdSchema , } , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { client , request , response } ) => { try { const { analyticsId } = request . params ; const { getAnalyticsAuditMessages } = analyticsAuditMessagesProvider ( client ) ; const results = await getAnalyticsAuditMessages ( analyticsId ) ; return response . ok ( { body : results , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/analytics/jobs_exist' , validate : { body : jobsExistSchema , } , options : { tags : [ 'access:ml:canGetDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { client , mlClient , request , response } ) => { try { const { analyticsIds , allSpaces } = request . body ; const results = { } ; for ( const id of analyticsIds ) { try { const { body } = allSpaces ? await client . asInternalUser . ml . getDataFrameAnalytics ( { id , } ) : await mlClient . getDataFrameAnalytics ( { id , } ) ; results [ id ] = { exists : body . data_frame_analytics . length > 0 } ; } catch ( error ) { if ( error . statusCode !== 404 ) { throw error ; } results [ id ] = { exists : false } ; } } return response . ok ( { body : results , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiParam router . get ( { path : '/api/ml/data_frame/analytics/map/{analyticsId}' , validate : { params : analyticsIdSchema , query : analyticsMapQuerySchema , } , } , routeGuard . fullLicenseAPIGuard ( async ( { mlClient , client , request , response } ) => { try { const { analyticsId } = request . params ; const treatAsRoot = request . query ?. treatAsRoot ; const type = request . query ?. type ; let results ; if ( treatAsRoot === 'true' || treatAsRoot === true ) { results = await getExtendedMap ( mlClient , client , { analyticsId : type !== JOB_MAP_NODE_TYPES . INDEX ? analyticsId : undefined , index : type === JOB_MAP_NODE_TYPES . INDEX ? analyticsId : undefined , } ) ; } else { results = await getAnalyticsMap ( mlClient , client , { analyticsId : type !== JOB_MAP_NODE_TYPES . TRAINED_MODEL ? analyticsId : undefined , modelId : type === JOB_MAP_NODE_TYPES . TRAINED_MODEL ? analyticsId : undefined , } ) ; } return response . ok ( { body : results , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription router . get ( { path : '/api/ml/data_frame/analytics/new_job_caps/{indexPattern}' , validate : { params : analyticsNewJobCapsParamsSchema , query : analyticsNewJobCapsQuerySchema , } , options : { tags : [ 'access:ml:canGetJobs' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { client , request , response , getDataViewsService } ) => { try { const { indexPattern } = request . params ; const isRollup = request . query ?. rollup === 'true' ; const dataViewsService = await getDataViewsService ( ) ; const fieldService = fieldServiceProvider ( indexPattern , isRollup , client , dataViewsService ) ; const { fields , aggs } = await fieldService . getData ( true ) ; convertForStringify ( aggs , fields ) ; return response . ok ( { body : { [ indexPattern ] : { aggs , fields , } , } , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; apiGroup api apiName apiDescription apiSchema router . post ( { path : '/api/ml/data_frame/analytics/validate' , validate : { body : dataAnalyticsJobConfigSchema , } , options : { tags : [ 'access:ml:canCreateDataFrameAnalytics' ] , } , } , routeGuard . fullLicenseAPIGuard ( async ( { client , request , response } ) => { const jobConfig = request . body ; try { const results = await validateAnalyticsJob ( client , jobConfig ) ; return response . ok ( { body : results , } ) ; } catch ( e ) { return response . customError ( wrapError ( e ) ) ; } } ) ) ; }