import { schema } from '@osd/config-schema' ; import { i18n } from '@osd/i18n' ; import defaultsDeep from 'lodash/defaultsDeep' ; import { Observable } from 'rxjs' ; import { first } from 'rxjs/operators' ; import { OpenSearchClient , IRouter , LegacyAPICaller , MetricsServiceSetup , ServiceStatus , ServiceStatusLevels , } from '../../../../../core/server' ; import { CollectorSet } from '../../collector' ; const STATS_NOT_READY_MESSAGE = i18n . translate ( 'usageCollection.stats.notReadyMessage' , { defaultMessage : 'Stats are not ready yet. Please try again later.' , } ) ; const SNAPSHOT_REGEX = /-snapshot/i ; export function registerStatsRoute ( { router , config , collectorSet , metrics , overallStatus$ , } ) { const getUsage = async ( callCluster , opensearchClient ) : Promise < any > => { const usage = await collectorSet . bulkFetchUsage ( callCluster , opensearchClient ) ; return collectorSet . toObject ( usage ) ; } ; const getClusterUuid = async ( callCluster ) : Promise < string > => { const { cluster_uuid : uuid } = await callCluster ( 'info' , { filterPath : 'cluster_uuid' } ) ; return uuid ; } ; router . get ( { path : '/api/stats' , options : { authRequired : ! config . allowAnonymous , tags : [ 'api' ] , } , validate : { query : schema . object ( { extended : schema . oneOf ( [ schema . literal ( '' ) , schema . boolean ( ) ] , { defaultValue : false } ) , legacy : schema . oneOf ( [ schema . literal ( '' ) , schema . boolean ( ) ] , { defaultValue : false } ) , exclude_usage : schema . oneOf ( [ schema . literal ( '' ) , schema . boolean ( ) ] , { defaultValue : false , } ) , } ) , } , } , async ( context , req , res ) => { const isExtended = req . query . extended === '' || req . query . extended ; const isLegacy = req . query . legacy === '' || req . query . legacy ; const shouldGetUsage = req . query . exclude_usage === false ; let extended ; if ( isExtended ) { const callCluster = context . core . opensearch . legacy . client . callAsCurrentUser ; const opensearchClient = context . core . opensearch . client . asCurrentUser ; if ( shouldGetUsage ) { const collectorsReady = await collectorSet . areAllCollectorsReady ( ) ; if ( ! collectorsReady ) { return res . customError ( { statusCode : 503 , body : { message : STATS_NOT_READY_MESSAGE } } ) ; } } const usagePromise = shouldGetUsage ? getUsage ( callCluster , opensearchClient ) : Promise . resolve ( { } ) ; const [ usage , clusterUuid ] = await Promise . all ( [ usagePromise , getClusterUuid ( callCluster ) ] ) ; let modifiedUsage = usage ; if ( isLegacy ) { modifiedUsage = Object . keys ( usage ) . reduce ( ( accum , usageKey ) => { if ( usageKey === 'opensearchDashboards' ) { accum = { ... accum , ... usage [ usageKey ] , } ; } else if ( usageKey === 'reporting' ) { accum = { ... accum , xpack : { ... accum . xpack , reporting : usage [ usageKey ] , } , } ; } else { defaultsDeep ( accum , { [ usageKey ] : usage [ usageKey ] } ) ; } return accum ; } , { } as any ) ; extended = { usage : modifiedUsage , clusterUuid , } ; } else { extended = collectorSet . toApiFieldNames ( { usage : modifiedUsage , clusterUuid , } ) ; } } const { collected_at : collectedAt , ... lastMetrics } = await metrics . getOpsMetrics$ ( ) . pipe ( first ( ) ) . toPromise ( ) ; const overallStatus = await overallStatus$ . pipe ( first ( ) ) . toPromise ( ) ; const opensearchDashboardsStats = collectorSet . toApiFieldNames ( { ... lastMetrics , opensearchDashboards : { uuid : config . uuid , name : config . server . name , index : config . opensearchDashboardsIndex , host : config . server . hostname , locale : i18n . getLocale ( ) , transport_address : `${ config . server . hostname }:${ config . server . port }` , version : config . opensearchDashboardsVersion . replace ( SNAPSHOT_REGEX , '' ) , snapshot : SNAPSHOT_REGEX . test ( config . opensearchDashboardsVersion ) , status : ServiceStatusToLegacyState [ overallStatus . level . toString ( ) ] , } , last_updated : collectedAt . toISOString ( ) , collection_interval_in_millis : metrics . collectionInterval , } ) ; return res . ok ( { body : { ... opensearchDashboardsStats , ... extended , } , } ) ; } ) ; } const ServiceStatusToLegacyState = { [ ServiceStatusLevels . critical . toString ( ) ] : 'red' , [ ServiceStatusLevels . unavailable . toString ( ) ] : 'red' , [ ServiceStatusLevels . degraded . toString ( ) ] : 'yellow' , [ ServiceStatusLevels . available . toString ( ) ] : 'green' , } ;