Updating flux time range re-queries data
parent
e15a8acef7
commit
2fba2b9721
|
@ -16,6 +16,7 @@ import {Source, AlertRule, QueryConfig, Query, TimeRange} from 'src/types'
|
|||
|
||||
import {ErrorHandling} from 'src/shared/decorators/errors'
|
||||
import {setHoverTime as setHoverTimeAction} from 'src/dashboards/actions'
|
||||
import uuid from 'uuid'
|
||||
|
||||
interface Props {
|
||||
source: Source
|
||||
|
@ -56,6 +57,7 @@ class RuleGraph extends PureComponent<Props> {
|
|||
timeRange={timeRange}
|
||||
source={source}
|
||||
queries={this.queries}
|
||||
uuid={uuid.v1()}
|
||||
>
|
||||
{data => {
|
||||
return (
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
import uuid from 'uuid'
|
||||
import {PureComponent} from 'react'
|
||||
|
||||
import {AutoRefresher} from 'src/utils/AutoRefresher'
|
||||
|
||||
interface Props {
|
||||
autoRefresh?: AutoRefresher
|
||||
manualRefresh?: number
|
||||
children: (uuid: string) => JSX.Element | JSX.Element[]
|
||||
}
|
||||
|
||||
interface State {
|
||||
id: string
|
||||
manualRefresh: number
|
||||
}
|
||||
|
||||
export default class AutoRefresh extends PureComponent<Props, State> {
|
||||
public static getDerivedStateFromProps(nextProps: Props, state: State) {
|
||||
if (state.manualRefresh !== nextProps.manualRefresh) {
|
||||
return {
|
||||
id: uuid.v4(),
|
||||
manualRefresh: nextProps.manualRefresh,
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
this.state = {
|
||||
manualRefresh: props.manualRefresh,
|
||||
id: uuid.v4(),
|
||||
}
|
||||
}
|
||||
|
||||
public render() {
|
||||
return this.props.children(this.state.id)
|
||||
}
|
||||
|
||||
public componentDidMount() {
|
||||
const {autoRefresh} = this.props
|
||||
|
||||
if (autoRefresh) {
|
||||
autoRefresh.subscribe(this.update)
|
||||
}
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
const {autoRefresh} = this.props
|
||||
|
||||
if (autoRefresh) {
|
||||
autoRefresh.unsubscribe(this.update)
|
||||
}
|
||||
}
|
||||
|
||||
private update = () => {
|
||||
this.setState({id: uuid.v4()})
|
||||
}
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
// Libraries
|
||||
import React, {PureComponent} from 'react'
|
||||
import React, {Component} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import _ from 'lodash'
|
||||
|
||||
|
@ -14,6 +14,7 @@ import TimeMachineTables from 'src/flux/components/TimeMachineTables'
|
|||
import RawFluxDataTable from 'src/shared/components/TimeMachine/RawFluxDataTable'
|
||||
import TableGraphTransform from 'src/shared/components/TableGraphTransform'
|
||||
import TableGraphFormat from 'src/shared/components/TableGraphFormat'
|
||||
import AutoRefresh from 'src/shared/components/AutoRefresh'
|
||||
|
||||
// Constants
|
||||
import {emptyGraphCopy} from 'src/shared/copy/cell'
|
||||
|
@ -24,7 +25,8 @@ import {
|
|||
import {DataType} from 'src/shared/constants'
|
||||
|
||||
// Utils
|
||||
import {AutoRefresher} from 'src/utils/AutoRefresher'
|
||||
import {AutoRefresher, GlobalAutoRefresher} from 'src/utils/AutoRefresher'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
// Actions
|
||||
import {setHoverTime} from 'src/dashboards/actions'
|
||||
|
@ -42,6 +44,7 @@ import {
|
|||
FluxTable,
|
||||
RemoteDataState,
|
||||
QueryUpdateState,
|
||||
QueryType,
|
||||
} from 'src/types'
|
||||
import {
|
||||
TableOptions,
|
||||
|
@ -50,7 +53,10 @@ import {
|
|||
NoteVisibility,
|
||||
} from 'src/types/dashboards'
|
||||
import {GrabDataForDownloadHandler} from 'src/types/layout'
|
||||
import {TimeSeriesServerResponse} from 'src/types/series'
|
||||
import {
|
||||
TimeSeriesSuccessfulResult,
|
||||
TimeSeriesServerResponse,
|
||||
} from 'src/types/series'
|
||||
|
||||
interface TypeAndData {
|
||||
dataType: DataType
|
||||
|
@ -91,25 +97,18 @@ interface Props {
|
|||
onUpdateFieldOptions?: (fieldOptions: FieldOption[]) => void
|
||||
}
|
||||
|
||||
class RefreshingGraph extends PureComponent<Props> {
|
||||
class RefreshingGraph extends Component<Props> {
|
||||
public static defaultProps: Partial<Props> = {
|
||||
inView: true,
|
||||
manualRefresh: 0,
|
||||
staticLegend: false,
|
||||
timeFormat: DEFAULT_TIME_FORMAT,
|
||||
decimalPlaces: DEFAULT_DECIMAL_PLACES,
|
||||
autoRefresher: GlobalAutoRefresher,
|
||||
}
|
||||
|
||||
private timeSeries: React.RefObject<TimeSeries> = React.createRef()
|
||||
|
||||
public componentDidUpdate(prevProps) {
|
||||
if (!this.timeSeries.current) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.props.editorLocation && this.haveVisOptionsChanged(prevProps)) {
|
||||
this.timeSeries.current.forceUpdate()
|
||||
}
|
||||
public shouldComponentUpdate(nextProps: Props) {
|
||||
return this.haveVisOptionsChanged(nextProps)
|
||||
}
|
||||
|
||||
public render() {
|
||||
|
@ -144,53 +143,107 @@ class RefreshingGraph extends PureComponent<Props> {
|
|||
}
|
||||
|
||||
return (
|
||||
<TimeSeries
|
||||
ref={this.timeSeries}
|
||||
autoRefresher={autoRefresher}
|
||||
manualRefresh={manualRefresh}
|
||||
source={source}
|
||||
cellType={type}
|
||||
inView={inView}
|
||||
queries={this.queries}
|
||||
timeRange={timeRange}
|
||||
templates={templates}
|
||||
editQueryStatus={editQueryStatus}
|
||||
onNotify={onNotify}
|
||||
grabDataForDownload={grabDataForDownload}
|
||||
grabFluxData={grabFluxData}
|
||||
cellNote={cellNote}
|
||||
cellNoteVisibility={cellNoteVisibility}
|
||||
>
|
||||
{({timeSeriesInfluxQL, timeSeriesFlux, rawFluxData, loading, uuid}) => {
|
||||
if (showRawFluxData) {
|
||||
return <RawFluxDataTable csv={rawFluxData} />
|
||||
}
|
||||
<AutoRefresh autoRefresh={autoRefresher} manualRefresh={manualRefresh}>
|
||||
{refreshingUUID => (
|
||||
<TimeSeries
|
||||
uuid={refreshingUUID}
|
||||
source={source}
|
||||
inView={inView}
|
||||
queries={this.queries}
|
||||
timeRange={timeRange}
|
||||
templates={templates}
|
||||
editQueryStatus={editQueryStatus}
|
||||
onNotify={onNotify}
|
||||
grabDataForDownload={grabDataForDownload}
|
||||
grabFluxData={grabFluxData}
|
||||
>
|
||||
{({
|
||||
timeSeriesInfluxQL,
|
||||
timeSeriesFlux,
|
||||
rawFluxData,
|
||||
loading,
|
||||
uuid,
|
||||
}) => {
|
||||
const hasValues =
|
||||
timeSeriesFlux.length ||
|
||||
_.some(timeSeriesInfluxQL, s => {
|
||||
const results = getDeep<TimeSeriesSuccessfulResult[]>(
|
||||
s,
|
||||
'response.results',
|
||||
[]
|
||||
)
|
||||
const v = _.some(results, r => r.series)
|
||||
return v
|
||||
})
|
||||
|
||||
switch (type) {
|
||||
case CellType.SingleStat:
|
||||
return this.singleStat(timeSeriesInfluxQL, timeSeriesFlux)
|
||||
case CellType.Table:
|
||||
return this.table(timeSeriesInfluxQL, timeSeriesFlux, uuid)
|
||||
case CellType.Gauge:
|
||||
return this.gauge(timeSeriesInfluxQL, timeSeriesFlux)
|
||||
default:
|
||||
return this.lineGraph(timeSeriesInfluxQL, timeSeriesFlux, loading)
|
||||
}
|
||||
}}
|
||||
</TimeSeries>
|
||||
if (!hasValues) {
|
||||
if (cellNoteVisibility === NoteVisibility.ShowWhenNoData) {
|
||||
return <MarkdownCell text={cellNote} />
|
||||
}
|
||||
|
||||
if (
|
||||
this.isFluxQuery &&
|
||||
!getDeep<string>(source, 'links.flux', null)
|
||||
) {
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<p>The current source does not support flux</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<p>No Results</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
if (showRawFluxData) {
|
||||
return <RawFluxDataTable csv={rawFluxData} />
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case CellType.SingleStat:
|
||||
return this.singleStat(timeSeriesInfluxQL, timeSeriesFlux)
|
||||
case CellType.Table:
|
||||
return this.table(timeSeriesInfluxQL, timeSeriesFlux, uuid)
|
||||
case CellType.Gauge:
|
||||
return this.gauge(timeSeriesInfluxQL, timeSeriesFlux)
|
||||
default:
|
||||
return this.lineGraph(
|
||||
timeSeriesInfluxQL,
|
||||
timeSeriesFlux,
|
||||
loading
|
||||
)
|
||||
}
|
||||
}}
|
||||
</TimeSeries>
|
||||
)}
|
||||
</AutoRefresh>
|
||||
)
|
||||
}
|
||||
|
||||
private get isFluxQuery(): boolean {
|
||||
const {queries} = this.props
|
||||
|
||||
return getDeep<string>(queries, '0.type', '') === QueryType.Flux
|
||||
}
|
||||
|
||||
private haveVisOptionsChanged(prevProps: Props): boolean {
|
||||
const visProps: string[] = [
|
||||
'axes',
|
||||
'colors',
|
||||
'type',
|
||||
'tableOptions',
|
||||
'fieldOptions',
|
||||
'decimalPlaces',
|
||||
'timeFormat',
|
||||
'showRawFluxData',
|
||||
'queries',
|
||||
'templates',
|
||||
'manualRefresh',
|
||||
'timeRange',
|
||||
]
|
||||
|
||||
const prevVisValues = _.pick(prevProps, visProps)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
// Library
|
||||
import React, {Component} from 'react'
|
||||
import React, {PureComponent} from 'react'
|
||||
import _ from 'lodash'
|
||||
import uuid from 'uuid'
|
||||
|
||||
// API
|
||||
import {executeQuery} from 'src/shared/apis/query'
|
||||
import {
|
||||
|
@ -17,7 +16,6 @@ import {
|
|||
Query,
|
||||
RemoteDataState,
|
||||
TimeRange,
|
||||
CellType,
|
||||
Status,
|
||||
FluxTable,
|
||||
QueryType,
|
||||
|
@ -26,8 +24,6 @@ import {TimeSeriesServerResponse} from 'src/types/series'
|
|||
import {GrabDataForDownloadHandler} from 'src/types/layout'
|
||||
|
||||
// Utils
|
||||
import {GlobalAutoRefresher, AutoRefresher} from 'src/utils/AutoRefresher'
|
||||
import {NoteVisibility} from 'src/types/dashboards'
|
||||
import {
|
||||
extractQueryWarningMessage,
|
||||
extractQueryErrorMessage,
|
||||
|
@ -36,9 +32,6 @@ import {notify} from 'src/shared/actions/notifications'
|
|||
import {fluxResponseTruncatedError} from 'src/shared/copy/notifications'
|
||||
import {getDeep} from 'src/utils/wrappers'
|
||||
|
||||
// Components
|
||||
import MarkdownCell from 'src/shared/components/MarkdownCell'
|
||||
|
||||
export const DEFAULT_TIME_SERIES = [{response: {results: []}}]
|
||||
|
||||
interface RenderProps {
|
||||
|
@ -51,19 +44,15 @@ interface RenderProps {
|
|||
|
||||
interface Props {
|
||||
source: Source
|
||||
cellType?: CellType
|
||||
manualRefresh?: number
|
||||
uuid: string
|
||||
queries: Query[]
|
||||
timeRange: TimeRange
|
||||
children: (r: RenderProps) => JSX.Element
|
||||
autoRefresher?: AutoRefresher
|
||||
inView?: boolean
|
||||
templates?: Template[]
|
||||
editQueryStatus?: (queryID: string, status: Status) => void
|
||||
grabDataForDownload?: GrabDataForDownloadHandler
|
||||
grabFluxData?: (data: FluxTable[]) => void
|
||||
cellNote?: string
|
||||
cellNoteVisibility?: NoteVisibility
|
||||
onNotify?: typeof notify
|
||||
}
|
||||
|
||||
|
@ -85,11 +74,10 @@ const GraphLoadingDots = () => (
|
|||
</div>
|
||||
)
|
||||
|
||||
class TimeSeries extends Component<Props, State> {
|
||||
class TimeSeries extends PureComponent<Props, State> {
|
||||
public static defaultProps = {
|
||||
inView: true,
|
||||
templates: [],
|
||||
autoRefresher: GlobalAutoRefresher,
|
||||
editQueryStatus: () => ({
|
||||
type: 'NOOP',
|
||||
payload: {},
|
||||
|
@ -112,8 +100,6 @@ class TimeSeries extends Component<Props, State> {
|
|||
return null
|
||||
}
|
||||
|
||||
private isComponentMounted: boolean = false
|
||||
|
||||
constructor(props: Props) {
|
||||
super(props)
|
||||
|
||||
|
@ -128,59 +114,25 @@ class TimeSeries extends Component<Props, State> {
|
|||
}
|
||||
}
|
||||
|
||||
public shouldComponentUpdate(prevProps: Props, prevState: State) {
|
||||
const propKeys = [
|
||||
'source',
|
||||
'queries',
|
||||
'timeRange',
|
||||
'inView',
|
||||
'templates',
|
||||
'cellType',
|
||||
'manualRefresh',
|
||||
]
|
||||
const stateKeys = ['loading', 'timeSeriesInfluxQL', 'timeSeriesFlux']
|
||||
|
||||
const propsUpdated = propKeys.some(
|
||||
k => !_.isEqual(this.props[k], prevProps[k])
|
||||
)
|
||||
|
||||
const stateUpdated = stateKeys.some(
|
||||
k => !_.isEqual(this.state[k], prevState[k])
|
||||
)
|
||||
|
||||
return propsUpdated || stateUpdated
|
||||
}
|
||||
|
||||
public async componentDidMount() {
|
||||
const {autoRefresher} = this.props
|
||||
|
||||
this.isComponentMounted = true
|
||||
this.executeQueries()
|
||||
autoRefresher.subscribe(this.executeQueries)
|
||||
}
|
||||
|
||||
public componentWillUnmount() {
|
||||
const {autoRefresher} = this.props
|
||||
|
||||
this.isComponentMounted = false
|
||||
autoRefresher.unsubscribe(this.executeQueries)
|
||||
}
|
||||
|
||||
public async componentDidUpdate(prevProps: Props) {
|
||||
if (this.props.autoRefresher !== prevProps.autoRefresher) {
|
||||
prevProps.autoRefresher.unsubscribe(this.executeQueries)
|
||||
this.props.autoRefresher.subscribe(this.executeQueries)
|
||||
}
|
||||
const prevQueries = _.map(prevProps.queries, q => q.text)
|
||||
const currQueries = _.map(this.props.queries, q => q.text)
|
||||
const queriesDifferent = !_.isEqual(prevQueries, currQueries)
|
||||
|
||||
if (!this.isPropsDifferent(prevProps)) {
|
||||
return
|
||||
if (
|
||||
this.props.uuid !== prevProps.uuid ||
|
||||
queriesDifferent ||
|
||||
this.state.isFirstFetch
|
||||
) {
|
||||
this.executeQueries()
|
||||
}
|
||||
|
||||
this.executeQueries()
|
||||
}
|
||||
|
||||
public render() {
|
||||
const {cellNoteVisibility, cellNote, source} = this.props
|
||||
const {
|
||||
timeSeriesInfluxQL,
|
||||
timeSeriesFlux,
|
||||
|
@ -194,34 +146,6 @@ class TimeSeries extends Component<Props, State> {
|
|||
return <div className="graph-empty">{this.spinner}</div>
|
||||
}
|
||||
|
||||
const hasValues =
|
||||
timeSeriesFlux.length ||
|
||||
_.some(timeSeriesInfluxQL, s => {
|
||||
const results = _.get(s, 'response.results', [])
|
||||
const v = _.some(results, r => r.series)
|
||||
return v
|
||||
})
|
||||
|
||||
if (!hasValues) {
|
||||
if (cellNoteVisibility === NoteVisibility.ShowWhenNoData) {
|
||||
return <MarkdownCell text={cellNote} />
|
||||
}
|
||||
|
||||
if (this.isFluxQuery && !getDeep(source, 'links.flux', null)) {
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<p>The current source does not support flux</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="graph-empty">
|
||||
<p>No Results</p>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{this.loadingDots}
|
||||
|
@ -285,7 +209,11 @@ class TimeSeries extends Component<Props, State> {
|
|||
|
||||
const latestUUID = uuid.v1()
|
||||
|
||||
this.setState({loading: RemoteDataState.Loading, latestUUID})
|
||||
this.setState({
|
||||
loading: RemoteDataState.Loading,
|
||||
latestUUID,
|
||||
isFirstFetch: false,
|
||||
})
|
||||
|
||||
try {
|
||||
if (this.isFluxQuery) {
|
||||
|
@ -299,10 +227,6 @@ class TimeSeries extends Component<Props, State> {
|
|||
responseUUID = _.get(timeSeriesInfluxQL, '0.response.uuid')
|
||||
}
|
||||
|
||||
if (!this.isComponentMounted) {
|
||||
return
|
||||
}
|
||||
|
||||
if (responseUUID !== this.state.latestUUID) {
|
||||
return
|
||||
}
|
||||
|
@ -316,7 +240,6 @@ class TimeSeries extends Component<Props, State> {
|
|||
timeSeriesInfluxQL,
|
||||
timeSeriesFlux,
|
||||
rawFluxData,
|
||||
isFirstFetch: false,
|
||||
loading,
|
||||
})
|
||||
|
||||
|
@ -393,25 +316,6 @@ class TimeSeries extends Component<Props, State> {
|
|||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
private isPropsDifferent(prevProps: Props) {
|
||||
const isSourceDifferent = !_.isEqual(this.props.source, prevProps.source)
|
||||
|
||||
return (
|
||||
this.props.manualRefresh !== prevProps.manualRefresh ||
|
||||
this.props.inView !== prevProps.inView ||
|
||||
!!this.queryDifference(this.props.queries, prevProps.queries).length ||
|
||||
!_.isEqual(this.props.templates, prevProps.templates) ||
|
||||
isSourceDifferent
|
||||
)
|
||||
}
|
||||
|
||||
private queryDifference = (left, right) => {
|
||||
const mapper = q => `${q.text}`
|
||||
const l = left.map(mapper)
|
||||
const r = right.map(mapper)
|
||||
return _.difference(_.union(l, r), _.intersection(l, r))
|
||||
}
|
||||
}
|
||||
|
||||
export default TimeSeries
|
||||
|
|
Loading…
Reference in New Issue