import { useEffect , useMemo , useState } from 'react' ; import { combineLatest , from , Observable , of , Subject } from 'rxjs' ; import { catchError , debounceTime , distinctUntilChanged , map , pluck , shareReplay , skipWhile , startWith , switchMap , tap , } from 'rxjs/operators' ; import { CoreStart } from 'kibana/public' ; import { TimeBuckets } from '../../application/util/time_buckets' ; import { MlStartDependencies } from '../../plugin' ; import { ANOMALY_SWIM_LANE_HARD_LIMIT , SWIM_LANE_DEFAULT_PAGE_SIZE , SWIMLANE_TYPE , SwimlaneType , } from '../../application/explorer/explorer_constants' ; import { UI_SETTINGS } from '../../../../../../src/plugins/data/public' ; import { OverallSwimlaneData } from '../../application/explorer/explorer_utils' ; import { isViewBySwimLaneData } from '../../application/explorer/swimlane_container' ; import { ViewMode } from '../../../../../../src/plugins/embeddable/public' ; import { AnomalySwimlaneEmbeddableInput , AnomalySwimlaneEmbeddableOutput , AnomalySwimlaneServices , } from '..' ; import { processFilters } from '../common/process_filters' ; import { CONTROLLED_BY_SWIM_LANE_FILTER } from '../..' ; import { getJobsObservable } from '../common/get_jobs_observable' ; const FETCH_RESULTS_DEBOUNCE_MS = 500 ; export function useSwimlaneInputResolver ( embeddableInput$ , onInputChange , refresh , services , chartWidth , fromPage ) { const [ { uiSettings } , , { anomalyTimelineService , anomalyDetectorService } ] = services ; const [ swimlaneData , setSwimlaneData ] = useState < OverallSwimlaneData > ( ) ; const [ swimlaneType , setSwimlaneType ] = useState < SwimlaneType > ( ) ; const [ error , setError ] = useState < Error | null > ( ) ; const [ perPage , setPerPage ] = useState < number | undefined > ( ) ; const [ isLoading , setIsLoading ] = useState ( false ) ; const chartWidth$ = useMemo ( ( ) => new Subject < number > ( ) , [ ] ) ; const selectedJobs$ = useMemo ( ( ) => { return getJobsObservable ( embeddableInput$ , anomalyDetectorService , setError ) . pipe ( shareReplay ( 1 ) ) ; } , [ ] ) ; const bucketInterval$ = useMemo ( ( ) => { return combineLatest ( [ selectedJobs$ , chartWidth$ , embeddableInput$ . pipe ( pluck ( 'timeRange' ) ) , ] ) . pipe ( skipWhile ( ( [ jobs , width ] ) => ! Array . isArray ( jobs ) || ! width ) , tap ( ( [ , , timeRange ] ) => { anomalyTimelineService . setTimeRange ( timeRange ) ; } ) , map ( ( [ jobs , width ] ) => anomalyTimelineService . getSwimlaneBucketInterval ( jobs ! , width ) ) , distinctUntilChanged ( ( prev , curr ) => { return prev . asSeconds ( ) === curr . asSeconds ( ) ; } ) ) ; } , [ ] ) ; const fromPage$ = useMemo ( ( ) => new Subject < number > ( ) , [ ] ) ; const perPage$ = useMemo ( ( ) => new Subject < number > ( ) , [ ] ) ; const timeBuckets = useMemo ( ( ) => { return new TimeBuckets ( { 'histogram:maxBars' : uiSettings . get ( UI_SETTINGS . HISTOGRAM_MAX_BARS ) , 'histogram:barTarget' : uiSettings . get ( UI_SETTINGS . HISTOGRAM_BAR_TARGET ) , dateFormat : uiSettings . get ( 'dateFormat' ) , 'dateFormat:scaled' : uiSettings . get ( 'dateFormat:scaled' ) , } ) ; } , [ ] ) ; useEffect ( ( ) => { const subscription = combineLatest ( [ selectedJobs$ , embeddableInput$ , bucketInterval$ , fromPage$ , perPage$ . pipe ( startWith ( undefined ) , distinctUntilChanged ( ( prev , curr ) => prev === undefined && curr === SWIM_LANE_DEFAULT_PAGE_SIZE ) ) , refresh . pipe ( startWith ( null ) ) , ] ) . pipe ( tap ( setIsLoading . bind ( null , true ) ) , debounceTime ( FETCH_RESULTS_DEBOUNCE_MS ) , switchMap ( ( [ explorerJobs , input , bucketInterval , fromPageInput , perPageFromState ] ) => { if ( ! explorerJobs ) { return of ( undefined ) ; } const { viewBy , swimlaneType : swimlaneTypeInput , perPage : perPageInput , filters , query , viewMode , } = input ; if ( ! swimlaneType ) { setSwimlaneType ( swimlaneTypeInput ) ; } let appliedFilters ; try { appliedFilters = processFilters ( filters , query , CONTROLLED_BY_SWIM_LANE_FILTER ) ; } catch ( e ) { setError ( e ) ; return of ( undefined ) ; } return from ( anomalyTimelineService . loadOverallData ( explorerJobs , undefined , bucketInterval ) ) . pipe ( switchMap ( ( overallSwimlaneData ) => { const { earliest , latest } = overallSwimlaneData ; if ( overallSwimlaneData && swimlaneTypeInput === SWIMLANE_TYPE . VIEW_BY ) { if ( perPageFromState === undefined ) { setPerPage ( perPageInput ?? SWIM_LANE_DEFAULT_PAGE_SIZE ) ; } if ( viewMode === ViewMode . EDIT && perPageFromState !== perPageInput ) { onInputChange ( { perPage : perPageFromState } ) ; } return from ( anomalyTimelineService . loadViewBySwimlane ( [ ] , { earliest , latest } , explorerJobs , viewBy ! , isViewBySwimLaneData ( swimlaneData ) ? swimlaneData . cardinality : ANOMALY_SWIM_LANE_HARD_LIMIT , perPageFromState ?? perPageInput ?? SWIM_LANE_DEFAULT_PAGE_SIZE , fromPageInput , undefined , appliedFilters , bucketInterval ) ) . pipe ( map ( ( viewBySwimlaneData ) => { return { ... viewBySwimlaneData ! , earliest , latest , } ; } ) ) ; } return of ( overallSwimlaneData ) ; } ) ) ; } ) , catchError ( ( e ) => { setError ( e . body ) ; return of ( undefined ) ; } ) ) . subscribe ( ( data ) => { if ( data !== undefined ) { setError ( null ) ; setSwimlaneData ( data ) ; setIsLoading ( false ) ; } } ) ; return ( ) => { subscription . unsubscribe ( ) ; } ; } , [ ] ) ; useEffect ( ( ) => { fromPage$ . next ( fromPage ) ; } , [ fromPage ] ) ; useEffect ( ( ) => { if ( perPage === undefined ) return ; perPage$ . next ( perPage ) ; } , [ perPage ] ) ; useEffect ( ( ) => { chartWidth$ . next ( chartWidth ) ; } , [ chartWidth ] ) ; return [ swimlaneType , swimlaneData , perPage ?? SWIM_LANE_DEFAULT_PAGE_SIZE , setPerPage , timeBuckets , isLoading , error , ] ; }