import { errorApiRef , useApi } from '@backstage/core-plugin-api' ; import { useAsync , useAsyncFn } from 'react-use' ; import { prometheusApiRef } from '../api' ; import { PrometheusDisplayableAlert , PrometheusMatrixVectorResult , PrometheusRuleResponse , PrometheusScalarStringResult , ResultType , } from '../types' ; import _ from 'lodash' ; import { DateTime } from 'luxon' ; function isMetricResult ( resultType , _result ) { return resultType === 'matrix' || resultType === 'vector' ; } export function resultToGraphData ( result , dimension ? ) { const keys = [ ] ; const metrics = { } ; if ( result . length === 0 ) { return { keys , metrics , data : [ ] } ; } const grouper = dimension && result . every ( it => Object . keys ( it . metric ) . includes ( dimension ) ) ? dimension : Object . keys ( result [ 0 ] . metric ) [ 0 ] ; const data = _ ( result ) . flatMap ( it => { if ( ! keys . includes ( it . metric [ grouper ] ) ) { keys . push ( it . metric [ grouper ] ) ; } if ( ! Object . keys ( metrics ) . includes ( it . metric [ grouper ] ) ) { metrics [ it . metric [ grouper ] ] = it . metric ; } return it . values . map ( val => ( { time : DateTime . fromSeconds ( val [ 0 ] ) . toISOTime ( ) , [ it . metric [ grouper ] ] : val [ 1 ] , } ) ) ; } ) . groupBy ( 'time' ) . map ( _ . spread ( _ . assign ) ) . map ( it => ( { ... it , time : DateTime . fromISO ( it . time ) . toSeconds ( ) } ) ) . value ( ) ; return { data , keys , metrics } ; } export function useMetrics ( { query , range , step , dimension , } ) { const prometheusApi = useApi ( prometheusApiRef ) ; const errorApi = useApi ( errorApiRef ) ; const [ state , fetchGraph ] = useAsyncFn ( async ( ) => { try { const value = await prometheusApi . query ( { query , range , step } ) ; if ( isMetricResult ( value . data . resultType , value . data . result ) ) { return resultToGraphData ( value . data . result , dimension ) ; } errorApi . post ( { name : 'Prometheus Graph construction error' , message : 'Only metric or vector result types are supported' , } ) ; return Promise . reject ( ) ; } catch ( e ) { errorApi . post ( { name : 'Prometheus API Error' , message : 'Failed to retrieve graph data from prometheus API' , } ) ; return Promise . reject ( e ) ; } } , [ ] ) ; return { loading : state . loading , error : state . error , value : state . value , fetchGraph , } ; } export function useAlerts ( alerts ) { const prometheusApi = useApi ( prometheusApiRef ) ; const { value , loading , error } = useAsync ( async ( ) : Promise < PrometheusRuleResponse > => { return await prometheusApi . getAlerts ( ) ; } , [ ] ) ; if ( value && value . status !== 'error' ) { const rules = value . data . groups . flatMap ( it => it . rules ) ; const displayableAlerts = rules . filter ( rule => alerts === 'all' || alerts . includes ( rule . name ) ) . flatMap ( rule => rule . alerts . map ( alert => ( { ... rule , ... alert , id : rule . name } ) ) ) ; return { loading , error , displayableAlerts } ; } else if ( value && value . status === 'error' ) { return { loading , error : { message : 'Error when retrieving alerting data from Prometheus' } , displayableAlerts : [ ] , } ; } return { loading , error , displayableAlerts : [ ] } ; }