D7net
Home
Console
Upload
information
Create File
Create Folder
About
Tools
:
/
proc
/
self
/
root
/
usr
/
share
/
grafana
/
public
/
app
/
features
/
query
/
components
/
Filename :
QueryGroupOptions.tsx
back
Copy
import { css } from '@emotion/css'; import React, { PureComponent, ChangeEvent, FocusEvent } from 'react'; import { rangeUtil, PanelData, DataSourceApi } from '@grafana/data'; import { Switch, Input, InlineFormLabel, stylesFactory } from '@grafana/ui'; import { QueryOperationRow } from 'app/core/components/QueryOperationRow/QueryOperationRow'; import { config } from 'app/core/config'; import { QueryGroupOptions } from 'app/types'; interface Props { options: QueryGroupOptions; dataSource: DataSourceApi; data: PanelData; onChange: (options: QueryGroupOptions) => void; } interface State { timeRangeFrom: string; timeRangeShift: string; timeRangeHide: boolean; isOpen: boolean; relativeTimeIsValid: boolean; timeShiftIsValid: boolean; } export class QueryGroupOptionsEditor extends PureComponent<Props, State> { constructor(props: Props) { super(props); const { options } = props; this.state = { timeRangeFrom: options.timeRange?.from || '', timeRangeShift: options.timeRange?.shift || '', timeRangeHide: options.timeRange?.hide ?? false, isOpen: false, relativeTimeIsValid: true, timeShiftIsValid: true, }; } onRelativeTimeChange = (event: ChangeEvent<HTMLInputElement>) => { this.setState({ timeRangeFrom: event.target.value, }); }; onTimeShiftChange = (event: ChangeEvent<HTMLInputElement>) => { this.setState({ timeRangeShift: event.target.value, }); }; onOverrideTime = (event: FocusEvent<HTMLInputElement>) => { const { options, onChange } = this.props; const newValue = emptyToNull(event.target.value); const isValid = timeRangeValidation(newValue); if (isValid && options.timeRange?.from !== newValue) { onChange({ ...options, timeRange: { ...(options.timeRange ?? {}), from: newValue, }, }); } this.setState({ relativeTimeIsValid: isValid }); }; onTimeShift = (event: FocusEvent<HTMLInputElement>) => { const { options, onChange } = this.props; const newValue = emptyToNull(event.target.value); const isValid = timeRangeValidation(newValue); if (isValid && options.timeRange?.shift !== newValue) { onChange({ ...options, timeRange: { ...(options.timeRange ?? {}), shift: newValue, }, }); } this.setState({ timeShiftIsValid: isValid }); }; onToggleTimeOverride = () => { const { onChange, options } = this.props; this.setState({ timeRangeHide: !this.state.timeRangeHide }, () => { onChange({ ...options, timeRange: { ...(options.timeRange ?? {}), hide: this.state.timeRangeHide, }, }); }); }; onCacheTimeoutBlur = (event: ChangeEvent<HTMLInputElement>) => { const { options, onChange } = this.props; onChange({ ...options, cacheTimeout: emptyToNull(event.target.value), }); }; onQueryCachingTTLBlur = (event: ChangeEvent<HTMLInputElement>) => { const { options, onChange } = this.props; let ttl: number | null = parseInt(event.target.value, 10); if (isNaN(ttl) || ttl === 0) { ttl = null; } onChange({ ...options, queryCachingTTL: ttl, }); }; onMaxDataPointsBlur = (event: ChangeEvent<HTMLInputElement>) => { const { options, onChange } = this.props; let maxDataPoints: number | null = parseInt(event.target.value as string, 10); if (isNaN(maxDataPoints) || maxDataPoints === 0) { maxDataPoints = null; } if (maxDataPoints !== options.maxDataPoints) { onChange({ ...options, maxDataPoints, }); } }; onMinIntervalBlur = (event: ChangeEvent<HTMLInputElement>) => { const { options, onChange } = this.props; const minInterval = emptyToNull(event.target.value); if (minInterval !== options.minInterval) { onChange({ ...options, minInterval, }); } }; renderCacheTimeoutOption() { const { dataSource, options } = this.props; const tooltip = `If your time series store has a query cache this option can override the default cache timeout. Specify a numeric value in seconds.`; if (!dataSource.meta.queryOptions?.cacheTimeout) { return null; } return ( <div className="gf-form-inline"> <div className="gf-form"> <InlineFormLabel width={9} tooltip={tooltip}> Cache timeout </InlineFormLabel> <Input type="text" className="width-6" placeholder="60" spellCheck={false} onBlur={this.onCacheTimeoutBlur} defaultValue={options.cacheTimeout ?? ''} /> </div> </div> ); } renderQueryCachingTTLOption() { const { dataSource, options } = this.props; const tooltip = `Cache time-to-live: How long results from this queries in this panel will be cached, in milliseconds. Defaults to the TTL in the caching configuration for this datasource.`; if (!dataSource.cachingConfig?.enabled) { return null; } return ( <div className="gf-form-inline"> <div className="gf-form"> <InlineFormLabel width={9} tooltip={tooltip}> Cache TTL </InlineFormLabel> <Input type="number" className="width-6" placeholder={`${dataSource.cachingConfig.TTLMs}`} spellCheck={false} onBlur={this.onQueryCachingTTLBlur} defaultValue={options.queryCachingTTL ?? undefined} /> </div> </div> ); } renderMaxDataPointsOption() { const { data, options } = this.props; const realMd = data.request?.maxDataPoints; const value = options.maxDataPoints ?? ''; const isAuto = value === ''; return ( <div className="gf-form-inline"> <div className="gf-form"> <InlineFormLabel width={9} tooltip={ <> The maximum data points per series. Used directly by some data sources and used in calculation of auto interval. With streaming data this value is used for the rolling buffer. </> } > Max data points </InlineFormLabel> <Input type="number" className="width-6" placeholder={`${realMd}`} spellCheck={false} onBlur={this.onMaxDataPointsBlur} defaultValue={value} /> {isAuto && ( <> <div className="gf-form-label query-segment-operator">=</div> <div className="gf-form-label">Width of panel</div> </> )} </div> </div> ); } renderIntervalOption() { const { data, dataSource, options } = this.props; const realInterval = data.request?.interval; const minIntervalOnDs = dataSource.interval ?? 'No limit'; return ( <> <div className="gf-form-inline"> <div className="gf-form"> <InlineFormLabel width={9} tooltip={ <> A lower limit for the interval. Recommended to be set to write frequency, for example <code>1m</code>{' '} if your data is written every minute. Default value can be set in data source settings for most data sources. </> } > Min interval </InlineFormLabel> <Input type="text" className="width-6" placeholder={`${minIntervalOnDs}`} spellCheck={false} onBlur={this.onMinIntervalBlur} defaultValue={options.minInterval ?? ''} /> </div> </div> <div className="gf-form-inline"> <div className="gf-form"> <InlineFormLabel width={9} tooltip={ <> The evaluated interval that is sent to data source and is used in <code>$__interval</code> and{' '} <code>$__interval_ms</code>. This value is not exactly equal to{' '} <code>Time range / max data points</code>, it will approximate a series of magic number. </> } > Interval </InlineFormLabel> <InlineFormLabel width={6}>{realInterval}</InlineFormLabel> <div className="gf-form-label query-segment-operator">=</div> <div className="gf-form-label">Time range / max data points</div> </div> </div> </> ); } onOpenOptions = () => { this.setState({ isOpen: true }); }; onCloseOptions = () => { this.setState({ isOpen: false }); }; renderCollapsedText(styles: StylesType): React.ReactNode | undefined { const { data, options } = this.props; const { isOpen } = this.state; if (isOpen) { return undefined; } let mdDesc = options.maxDataPoints ?? ''; if (mdDesc === '' && data.request) { mdDesc = `auto = ${data.request.maxDataPoints}`; } let intervalDesc = options.minInterval; if (data.request) { intervalDesc = `${data.request.interval}`; } return ( <> {<div className={styles.collapsedText}>MD = {mdDesc}</div>} {<div className={styles.collapsedText}>Interval = {intervalDesc}</div>} </> ); } render() { const { timeRangeHide: hideTimeOverride, relativeTimeIsValid, timeShiftIsValid } = this.state; const { timeRangeFrom: relativeTime, timeRangeShift: timeShift, isOpen } = this.state; const styles = getStyles(); return ( <QueryOperationRow id="Query options" index={0} title="Query options" headerElement={this.renderCollapsedText(styles)} isOpen={isOpen} onOpen={this.onOpenOptions} onClose={this.onCloseOptions} > {this.renderMaxDataPointsOption()} {this.renderIntervalOption()} {this.renderCacheTimeoutOption()} {this.renderQueryCachingTTLOption()} <div className="gf-form"> <InlineFormLabel width={9} tooltip={ <> Overrides the relative time range for individual panels, which causes them to be different than what is selected in the dashboard time picker in the top-right corner of the dashboard. For example to configure the Last 5 minutes the Relative time should be <code>now-5m</code> and <code>5m</code>, or variables like <code>$_relativeTime</code>. </> } > Relative time </InlineFormLabel> <Input type="text" className="width-6" placeholder="1h" onChange={this.onRelativeTimeChange} onBlur={this.onOverrideTime} invalid={!relativeTimeIsValid} value={relativeTime} /> </div> <div className="gf-form"> <InlineFormLabel width={9} tooltip={ <> Overrides the time range for individual panels by shifting its start and end relative to the time picker. For example to configure the Last 1h the Time shift should be <code>now-1h</code> and{' '} <code>1h</code>, or variables like <code>$_timeShift</code>. </> } > Time shift </InlineFormLabel> <Input type="text" className="width-6" placeholder="1h" onChange={this.onTimeShiftChange} onBlur={this.onTimeShift} invalid={!timeShiftIsValid} value={timeShift} /> </div> {(timeShift || relativeTime) && ( <div className="gf-form-inline align-items-center"> <InlineFormLabel width={9}>Hide time info</InlineFormLabel> <Switch value={hideTimeOverride} onChange={this.onToggleTimeOverride} /> </div> )} </QueryOperationRow> ); } } const timeRangeValidation = (value: string | null) => { if (!value) { return true; } return rangeUtil.isValidTimeSpan(value); }; const emptyToNull = (value: string) => { return value === '' ? null : value; }; const getStyles = stylesFactory(() => { const { theme } = config; return { collapsedText: css` margin-left: ${theme.spacing.md}; font-size: ${theme.typography.size.sm}; color: ${theme.colors.textWeak}; `, }; }); type StylesType = ReturnType<typeof getStyles>;