import { i18n } from '@kbn/i18n' ; import { distinctUntilChanged , filter , map , pluck , switchMap , startWith } from 'rxjs/operators' ; import { combineLatest , Observable , Subject } from 'rxjs' ; import { CardinalityModelPlotHigh , CardinalityValidationResult , ml , } from '../../../../services/ml_api_service' ; import { JobCreator } from '../job_creator' ; import { CombinedJob } from '../../../../../../common/types/anomaly_detection_jobs' ; import { BasicValidations } from './job_validator' ; export enum VALIDATOR_SEVERITY { ERROR , WARNING } export interface CardinalityValidatorError { highCardinality : { value : number ; severity : VALIDATOR_SEVERITY ; } ; } const jobExistsErrorMessage = i18n . translate ( 'xpack.ml.newJob.wizard.validateJob.asyncJobNameAlreadyExists' , { defaultMessage : 'Job ID already exists. A job ID cannot be the same as an existing job or group.' , } ) ; const groupExistsErrorMessage = i18n . translate ( 'xpack.ml.newJob.wizard.validateJob.asyncGroupNameAlreadyExists' , { defaultMessage : 'Group ID already exists. A group ID cannot be the same as an existing group or job.' , } ) ; export type CardinalityValidatorResult = CardinalityValidatorError | null ; export type JobExistsResult = { jobIdExists : BasicValidations [ 'jobId' ] ; } | null ; export type GroupsExistResult = { groupIdsExist : BasicValidations [ 'groupIds' ] ; } | null ; export function isCardinalityModelPlotHigh ( cardinalityValidationResult ) { return ( ( cardinalityValidationResult as CardinalityModelPlotHigh ) . modelPlotCardinality !== undefined ) ; } export function cardinalityValidator ( jobCreator$ ) { return combineLatest ( [ jobCreator$ . pipe ( pluck ( 'modelPlot' ) ) , jobCreator$ . pipe ( filter ( ( jobCreator ) => { return jobCreator ?. modelPlot ; } ) , map ( ( jobCreator ) => { return { jobCreator , analysisConfigString : JSON . stringify ( jobCreator . jobConfig . analysis_config , null , 2 ) , } ; } ) , distinctUntilChanged ( ( prev , curr ) => { return prev . analysisConfigString === curr . analysisConfigString ; } ) , switchMap ( ( { jobCreator } ) => { return ml . validateCardinality$ ( { ... jobCreator . jobConfig , datafeed_config : jobCreator . datafeedConfig , } as CombinedJob ) . pipe ( map ( ( validationResults ) => { for ( const validationResult of validationResults ) { if ( isCardinalityModelPlotHigh ( validationResult ) ) { return { highCardinality : { value : validationResult . modelPlotCardinality , severity : VALIDATOR_SEVERITY . WARNING , } , } ; } } return null ; } ) ) ; } ) , startWith ( null ) ) , ] ) . pipe ( map ( ( [ isModelPlotEnabled , cardinalityValidationResult ] ) => { return isModelPlotEnabled ? cardinalityValidationResult : null ; } ) ) ; } export function jobIdValidator ( jobCreator$ ) { return jobCreator$ . pipe ( map ( ( jobCreator ) => { return jobCreator . jobId ; } ) , distinctUntilChanged ( ( prevJobId , currJobId ) => prevJobId === currJobId ) , switchMap ( ( jobId ) => ml . jobs . jobsExist$ ( [ jobId ] , true ) ) , map ( ( jobExistsResults ) => { const jobs = Object . values ( jobExistsResults ) ; const valid = jobs ?. [ 0 ] . exists === false ; return { jobIdExists : { valid , ... ( valid ? { } : { message : jobExistsErrorMessage } ) , } , } ; } ) ) ; } export function groupIdsValidator ( jobCreator$ ) { return jobCreator$ . pipe ( map ( ( jobCreator ) => jobCreator . groups ) , distinctUntilChanged ( ( prevGroups , currGroups ) => JSON . stringify ( prevGroups ) === JSON . stringify ( currGroups ) ) , switchMap ( ( groups ) => { return ml . jobs . jobsExist$ ( groups , true ) ; } ) , map ( ( jobExistsResults ) => { const groups = Object . values ( jobExistsResults ) ; const valid = groups . some ( ( g ) => g . exists === true && g . isGroup === false ) === false ; return { groupIdsExist : { valid , ... ( valid ? { } : { message : groupExistsErrorMessage } ) , } , } ; } ) ) ; }