2019-10-17 05:38:09 -07:00
import React , { Component } from 'react' ;
import { Alert , Button , Col , Row } from 'reactstrap' ;
2019-10-17 07:34:02 -07:00
import Panel , { PanelOptions , PanelDefaultOptions } from './Panel' ;
2019-10-17 05:38:09 -07:00
import { decodePanelOptionsFromQueryString , encodePanelOptionsToQueryString } from './utils/urlParams' ;
interface PanelListState {
panels : {
key : string ;
options : PanelOptions ;
} [ ] ,
metricNames : string [ ] ;
fetchMetricsError : string | null ;
timeDriftError : string | null ;
}
class PanelList extends Component < any , PanelListState > {
private key : number = 0 ;
constructor ( props : any ) {
super ( props ) ;
const urlPanels = decodePanelOptionsFromQueryString ( window . location . search ) ;
this . state = {
panels : urlPanels.length !== 0 ? urlPanels : [
{
key : this.getKey ( ) ,
options : PanelDefaultOptions ,
} ,
] ,
metricNames : [ ] ,
fetchMetricsError : null ,
timeDriftError : null ,
} ;
}
componentDidMount() {
fetch ( "../../api/v1/label/__name__/values" , { cache : "no-store" } )
. then ( resp = > {
if ( resp . ok ) {
return resp . json ( ) ;
} else {
throw new Error ( 'Unexpected response status when fetching metric names: ' + resp . statusText ) ; // TODO extract error
}
} )
. then ( json = > this . setState ( { metricNames : json.data } ) )
. catch ( error = > this . setState ( { fetchMetricsError : error.message } ) ) ;
const browserTime = new Date ( ) . getTime ( ) / 1000 ;
fetch ( "../../api/v1/query?query=time()" , { cache : "no-store" } )
. then ( resp = > {
if ( resp . ok ) {
return resp . json ( ) ;
} else {
throw new Error ( 'Unexpected response status when fetching metric names: ' + resp . statusText ) ; // TODO extract error
}
} )
. then ( json = > {
const serverTime = json . data . result [ 0 ] ;
const delta = Math . abs ( browserTime - serverTime ) ;
if ( delta >= 30 ) {
throw new Error ( 'Detected ' + delta + ' seconds time difference between your browser and the server. Prometheus relies on accurate time and time drift might cause unexpected query results.' ) ;
}
} )
. catch ( error = > this . setState ( { timeDriftError : error.message } ) ) ;
window . onpopstate = ( ) = > {
const panels = decodePanelOptionsFromQueryString ( window . location . search ) ;
if ( panels . length !== 0 ) {
this . setState ( { panels : panels } ) ;
}
}
}
getKey ( ) : string {
return ( this . key ++ ) . toString ( ) ;
}
handleOptionsChanged ( key : string , opts : PanelOptions ) : void {
const newPanels = this . state . panels . map ( p = > {
if ( key === p . key ) {
return {
key : key ,
options : opts ,
}
}
return p ;
} ) ;
this . setState ( { panels : newPanels } , this . updateURL )
}
updateURL ( ) : void {
const query = encodePanelOptionsToQueryString ( this . state . panels ) ;
2019-10-17 07:34:02 -07:00
window . history . pushState ( { } , '' , query ) ;
2019-10-17 05:38:09 -07:00
}
addPanel = ( ) : void = > {
const panels = this . state . panels . slice ( ) ;
panels . push ( {
key : this.getKey ( ) ,
options : PanelDefaultOptions ,
} ) ;
this . setState ( { panels : panels } , this . updateURL ) ;
}
removePanel = ( key : string ) : void = > {
const panels = this . state . panels . filter ( panel = > {
return panel . key !== key ;
} ) ;
this . setState ( { panels : panels } , this . updateURL ) ;
}
render() {
return (
< >
< Row >
< Col >
{ this . state . timeDriftError && < Alert color = "danger" > < strong > Warning : < / strong > Error fetching server time : { this . state . timeDriftError } < / Alert > }
< / Col >
< / Row >
< Row >
< Col >
{ this . state . fetchMetricsError && < Alert color = "danger" > < strong > Warning : < / strong > Error fetching metrics list : { this . state . fetchMetricsError } < / Alert > }
< / Col >
< / Row >
{ this . state . panels . map ( p = >
< Panel
key = { p . key }
options = { p . options }
onOptionsChanged = { ( opts : PanelOptions ) = > this . handleOptionsChanged ( p . key , opts ) }
removePanel = { ( ) = > this . removePanel ( p . key ) }
metricNames = { this . state . metricNames }
/ >
) }
< Button color = "primary" className = "add-panel-btn" onClick = { this . addPanel } > Add Panel < / Button >
< / >
) ;
}
}
export default PanelList ;