import Boom from '@hapi/boom' ; import { IScopedClusterClient , KibanaRequest } from 'kibana/server' ; import type { JobSavedObjectService } from './service' ; import { JobType , DeleteJobCheckResponse } from '../../common/types/saved_objects' ; import { DataFrameAnalyticsConfig } from '../../common/types/data_frame_analytics' ; import { ResolveMlCapabilities } from '../../common/types/capabilities' ; interface JobSavedObjectStatus { jobId : string ; type : JobType ; datafeedId ? : string | null ; namespaces : string [ ] | undefined ; checks : { jobExists : boolean ; datafeedExists ? : boolean ; } ; } export interface JobStatus { jobId : string ; datafeedId ? : string | null ; checks : { savedObjectExits : boolean ; } ; } export interface StatusResponse { savedObjects : { [ type in JobType ] : JobSavedObjectStatus [ ] ; } ; jobs : { [ type in JobType ] : JobStatus [ ] ; } ; } export function checksFactory ( client , jobSavedObjectService ) { async function checkStatus ( ) { const jobObjects = await jobSavedObjectService . getAllJobObjects ( undefined , false ) ; const { body : adJobs } = await client . asInternalUser . ml . getJobs ( ) ; const { body : datafeeds } = await client . asInternalUser . ml . getDatafeeds ( ) ; const { body : dfaJobs } = ( await client . asInternalUser . ml . getDataFrameAnalytics ( ) ) as unknown as { body : { data_frame_analytics : DataFrameAnalyticsConfig [ ] ; } ; } ; const savedObjectsStatus = jobObjects . map ( ( { attributes , namespaces } ) => { const type = attributes . type ; const jobId = attributes . job_id ; const datafeedId = type === 'anomaly-detector' ? attributes . datafeed_id : undefined ; let jobExists = false ; let datafeedExists ; if ( type === 'anomaly-detector' ) { jobExists = adJobs . jobs . some ( ( j ) => j . job_id === jobId ) ; datafeedExists = datafeeds . datafeeds . some ( ( d ) => d . job_id === jobId ) ; } else { jobExists = dfaJobs . data_frame_analytics . some ( ( j ) => j . id === jobId ) ; } return { jobId , type , datafeedId , namespaces , checks : { jobExists , datafeedExists , } , } ; } ) ; const allJobObjects = await jobSavedObjectService . getAllJobObjectsForAllSpaces ( ) ; const nonSpaceADObjectIds = new Set ( allJobObjects . filter ( ( { attributes } ) => attributes . type === 'anomaly-detector' ) . map ( ( { attributes } ) => attributes . job_id ) ) ; const nonSpaceDFAObjectIds = new Set ( allJobObjects . filter ( ( { attributes } ) => attributes . type === 'data-frame-analytics' ) . map ( ( { attributes } ) => attributes . job_id ) ) ; const adObjectIds = new Set ( savedObjectsStatus . filter ( ( { type } ) => type === 'anomaly-detector' ) . map ( ( { jobId } ) => jobId ) ) ; const dfaObjectIds = new Set ( savedObjectsStatus . filter ( ( { type } ) => type === 'data-frame-analytics' ) . map ( ( { jobId } ) => jobId ) ) ; const anomalyDetectors = adJobs . jobs . filter ( ( { job_id : jobId } ) => { return adObjectIds . has ( jobId ) === true || nonSpaceADObjectIds . has ( jobId ) === false ; } ) . map ( ( { job_id : jobId } ) => { const datafeedId = datafeeds . datafeeds . find ( ( df ) => df . job_id === jobId ) ?. datafeed_id ; return { jobId , datafeedId : datafeedId ?? null , checks : { savedObjectExits : nonSpaceADObjectIds . has ( jobId ) , } , } ; } ) ; const dataFrameAnalytics = dfaJobs . data_frame_analytics . filter ( ( { id : jobId } ) => { return dfaObjectIds . has ( jobId ) === true || nonSpaceDFAObjectIds . has ( jobId ) === false ; } ) . map ( ( { id : jobId } ) => { return { jobId , datafeedId : null , checks : { savedObjectExits : nonSpaceDFAObjectIds . has ( jobId ) , } , } ; } ) ; return { savedObjects : { 'anomaly-detector' : savedObjectsStatus . filter ( ( { type } ) => type === 'anomaly-detector' ) , 'data-frame-analytics' : savedObjectsStatus . filter ( ( { type } ) => type === 'data-frame-analytics' ) , } , jobs : { 'anomaly-detector' : anomalyDetectors , 'data-frame-analytics' : dataFrameAnalytics , } , } ; } async function canDeleteJobs ( request , jobType , jobIds , spacesEnabled , resolveMlCapabilities ) { if ( jobType !== 'anomaly-detector' && jobType !== 'data-frame-analytics' ) { throw Boom . badRequest ( 'Job type must be "anomaly-detector" or "data-frame-analytics"' ) ; } const mlCapabilities = await resolveMlCapabilities ( request ) ; if ( mlCapabilities === null ) { throw Boom . internal ( 'mlCapabilities is not defined' ) ; } if ( ( jobType === 'anomaly-detector' && mlCapabilities . canDeleteJob === false ) || ( jobType === 'data-frame-analytics' && mlCapabilities . canDeleteDataFrameAnalytics === false ) ) { return jobIds . reduce ( ( results , jobId ) => { results [ jobId ] = { canDelete : false , canRemoveFromSpace : false , } ; return results ; } , { } as DeleteJobCheckResponse ) ; } if ( spacesEnabled === false ) { return jobIds . reduce ( ( results , jobId ) => { results [ jobId ] = { canDelete : true , canRemoveFromSpace : false , } ; return results ; } , { } as DeleteJobCheckResponse ) ; } const canCreateGlobalJobs = await jobSavedObjectService . canCreateGlobalJobs ( request ) ; const jobObjects = await Promise . all ( jobIds . map ( ( id ) => jobSavedObjectService . getJobObject ( jobType , id ) ) ) ; return jobIds . reduce ( ( results , jobId ) => { const jobObject = jobObjects . find ( ( j ) => j ?. attributes . job_id === jobId ) ; if ( jobObject === undefined || jobObject . namespaces === undefined ) { results [ jobId ] = { canDelete : false , canRemoveFromSpace : false , } ; return results ; } const { namespaces } = jobObject ; const isGlobalJob = namespaces . includes ( '*' ) ; if ( canCreateGlobalJobs && isGlobalJob ) { results [ jobId ] = { canDelete : true , canRemoveFromSpace : false , } ; return results ; } if ( isGlobalJob ) { results [ jobId ] = { canDelete : false , canRemoveFromSpace : false , } ; return results ; } const canRemoveFromSpace = namespaces . length > 1 ; if ( namespaces . includes ( '?' ) ) { results [ jobId ] = { canDelete : false , canRemoveFromSpace , } ; return results ; } results [ jobId ] = { canDelete : true , canRemoveFromSpace , } ; return results ; } , { } as DeleteJobCheckResponse ) ; } return { checkStatus , canDeleteJobs } ; }