import { Observable , combineLatest , Subscription } from 'rxjs' ; import { map , distinctUntilChanged , shareReplay , take , debounceTime } from 'rxjs/operators' ; import { isDeepStrictEqual } from 'util' ; import { CoreService } from '../../types' ; import { CoreContext } from '../core_context' ; import { Logger } from '../logging' ; import { InternalOpenSearchServiceSetup } from '../opensearch' ; import { InternalHttpServiceSetup } from '../http' ; import { InternalSavedObjectsServiceSetup } from '../saved_objects' ; import { PluginName } from '../plugins' ; import { InternalMetricsServiceSetup } from '../metrics' ; import { registerStatusRoute } from './routes' ; import { InternalEnvironmentServiceSetup } from '../environment' ; import { config , StatusConfigType } from './status_config' ; import { ServiceStatus , CoreStatus , InternalStatusServiceSetup } from './types' ; import { getSummaryStatus } from './get_summary_status' ; import { PluginsStatusService } from './plugins_status' ; interface SetupDeps { opensearch : Pick < InternalOpenSearchServiceSetup , 'status$' > ; environment : InternalEnvironmentServiceSetup ; pluginDependencies : ReadonlyMap < PluginName , PluginName [ ] > ; http : InternalHttpServiceSetup ; metrics : InternalMetricsServiceSetup ; savedObjects : Pick < InternalSavedObjectsServiceSetup , 'status$' > ; } export class StatusService implements CoreService < InternalStatusServiceSetup > { private readonly logger : Logger ; private readonly config$ : Observable < StatusConfigType > ; private pluginsStatus ? : PluginsStatusService ; private overallSubscription ? : Subscription ; constructor ( private readonly coreContext ) { this . logger = coreContext . logger . get ( 'status' ) ; this . config$ = coreContext . configService . atPath < StatusConfigType > ( config . path ) ; } public async setup ( { opensearch , pluginDependencies , http , metrics , savedObjects , environment , } ) { const statusConfig = await this . config$ . pipe ( take ( 1 ) ) . toPromise ( ) ; const core$ = this . setupCoreStatus ( { opensearch , savedObjects } ) ; this . pluginsStatus = new PluginsStatusService ( { core$ , pluginDependencies } ) ; const overall$ = combineLatest ( [ core$ , this . pluginsStatus . getAll$ ( ) , ] ) . pipe ( debounceTime ( 500 ) , map ( ( [ coreStatus , pluginsStatus ] ) => { const summary = getSummaryStatus ( [ ... Object . entries ( coreStatus ) , ... Object . entries ( pluginsStatus ) , ] ) ; this . logger . debug ( `Recalculated overall status` , { status : summary } ) ; return summary ; } ) , distinctUntilChanged ( isDeepStrictEqual ) , shareReplay ( 1 ) ) ; this . overallSubscription = overall$ . subscribe ( ) ; const router = http . createRouter ( '' ) ; registerStatusRoute ( { router , config : { allowAnonymous : statusConfig . allowAnonymous , packageInfo : this . coreContext . env . packageInfo , serverName : http . getServerInfo ( ) . name , uuid : environment . instanceUuid , } , metrics , status : { overall$ , plugins$ : this . pluginsStatus . getAll$ ( ) , core$ , } , } ) ; return { core$ , overall$ , plugins : { set : this . pluginsStatus . set . bind ( this . pluginsStatus ) , getDependenciesStatus$ : this . pluginsStatus . getDependenciesStatus$ . bind ( this . pluginsStatus ) , getDerivedStatus$ : this . pluginsStatus . getDerivedStatus$ . bind ( this . pluginsStatus ) , } , isStatusPageAnonymous : ( ) => statusConfig . allowAnonymous , } ; } public start ( ) { } public stop ( ) { if ( this . overallSubscription ) { this . overallSubscription . unsubscribe ( ) ; this . overallSubscription = undefined ; } } private setupCoreStatus ( { opensearch , savedObjects , } ) { return combineLatest ( [ opensearch . status$ , savedObjects . status$ ] ) . pipe ( map ( ( [ opensearchStatus , savedObjectsStatus ] ) => ( { opensearch : opensearchStatus , savedObjects : savedObjectsStatus , } ) ) , distinctUntilChanged ( isDeepStrictEqual ) , shareReplay ( 1 ) ) ; } }