import React, { Component } from 'react'; import { Alert, Button, Col, Row } from 'reactstrap'; import Panel, { PanelOptions, PanelDefaultOptions } from './Panel'; 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 { 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); window.history.pushState({}, '', query); } 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 ( <> {this.state.timeDriftError && Warning: Error fetching server time: {this.state.timeDriftError}} {this.state.fetchMetricsError && Warning: Error fetching metrics list: {this.state.fetchMetricsError}} {this.state.panels.map(p => this.handleOptionsChanged(p.key, opts)} removePanel={() => this.removePanel(p.key)} metricNames={this.state.metricNames} /> )} ); } } export default PanelList;