diff --git a/ui/src/shared/apis/v2/query.ts b/ui/src/shared/apis/v2/query.ts index 78248d22ec..32388a595a 100644 --- a/ui/src/shared/apis/v2/query.ts +++ b/ui/src/shared/apis/v2/query.ts @@ -1,3 +1,5 @@ +import _ from 'lodash' + import Deferred from 'src/utils/Deferred' import {InfluxLanguage} from 'src/types/v2/dashboards' @@ -19,8 +21,21 @@ interface XHRError extends Error { export const executeQuery = async ( url: string, query: string, - language: InfluxLanguage = InfluxLanguage.Flux + language: InfluxLanguage = InfluxLanguage.Flux, + variables?: {[key: string]: string} ): Promise => { + let preamble = '' + + if (variables && language === InfluxLanguage.Flux) { + preamble = _.reduce( + variables, + (result, value, name) => `${result}${name} = ${value}\n`, + '' + ) + } + + query = `${preamble}${query}` + // We're using `XMLHttpRequest` directly here rather than through `axios` so // that we can poll the response size as it comes back. If the response size // is greater than a predefined limit, we close the HTTP connection and @@ -133,10 +148,11 @@ export const executeQuery = async ( } export const executeQueries = async ( - queries: URLQuery[] + queries: URLQuery[], + variables?: {[key: string]: string} ): Promise => { const promise = Promise.all( - queries.map(({url, text, type}) => executeQuery(url, text, type)) + queries.map(({url, text, type}) => executeQuery(url, text, type, variables)) ) return promise diff --git a/ui/src/shared/components/Conditional.tsx b/ui/src/shared/components/Conditional.tsx new file mode 100644 index 0000000000..2317c97b93 --- /dev/null +++ b/ui/src/shared/components/Conditional.tsx @@ -0,0 +1,15 @@ +const Conditional = ({ + isRendered, + children, +}: { + isRendered: boolean + children: JSX.Element +}): JSX.Element => { + if (isRendered) { + return children + } + + return null +} + +export default Conditional diff --git a/ui/src/shared/components/RefreshingView.tsx b/ui/src/shared/components/RefreshingView.tsx index 4b9f4af714..c9a5b3cd58 100644 --- a/ui/src/shared/components/RefreshingView.tsx +++ b/ui/src/shared/components/RefreshingView.tsx @@ -9,6 +9,7 @@ import QueryViewSwitcher from 'src/shared/components/QueryViewSwitcher' // Utils import {GlobalAutoRefresher} from 'src/utils/AutoRefresher' +import {timeRangeVariables} from 'src/shared/utils/queryBuilder' // Types import {TimeRange} from 'src/types' @@ -65,6 +66,7 @@ class RefreshingView extends PureComponent { submitToken={submitToken} queries={this.queries} key={manualRefresh} + variables={{...timeRangeVariables(timeRange)}} > {({tables, loading, error, isInitialFetch}) => { return ( diff --git a/ui/src/shared/components/TimeMachine.tsx b/ui/src/shared/components/TimeMachine.tsx index 9f5d905dc1..10d0f8c3f7 100644 --- a/ui/src/shared/components/TimeMachine.tsx +++ b/ui/src/shared/components/TimeMachine.tsx @@ -15,9 +15,10 @@ const INITIAL_RESIZER_HANDLE = 0.6 // Utils import {getActiveTimeMachine} from 'src/shared/selectors/timeMachines' +import {timeRangeVariables} from 'src/shared/utils/queryBuilder' // Types -import {AppState, DashboardQuery} from 'src/types/v2' +import {AppState, DashboardQuery, TimeRange} from 'src/types/v2' // Styles import './TimeMachine.scss' @@ -25,6 +26,7 @@ import './TimeMachine.scss' interface StateProps { queries: DashboardQuery[] submitToken: number + timeRange: TimeRange } interface State { @@ -43,7 +45,7 @@ class TimeMachine extends Component { } public render() { - const {queries, submitToken} = this.props + const {queries, submitToken, timeRange} = this.props const {resizerHandlePosition} = this.state return ( @@ -52,6 +54,7 @@ class TimeMachine extends Component { queries={queries} submitToken={submitToken} implicitSubmit={false} + variables={{...timeRangeVariables(timeRange)}} > {queriesState => ( { const mstp = (state: AppState) => { const timeMachine = getActiveTimeMachine(state) + const {timeRange} = timeMachine const queries = get(timeMachine, 'view.properties.queries', []) const submitToken = timeMachine.submitToken - return {queries, submitToken} + return {queries, submitToken, timeRange} } export default connect( diff --git a/ui/src/shared/components/TimeSeries.tsx b/ui/src/shared/components/TimeSeries.tsx index 08ee1b53ca..9528d1572a 100644 --- a/ui/src/shared/components/TimeSeries.tsx +++ b/ui/src/shared/components/TimeSeries.tsx @@ -33,6 +33,7 @@ interface StateProps { interface OwnProps { queries: DashboardQuery[] + variables?: {[key: string]: string} submitToken: number implicitSubmit?: boolean inView?: boolean @@ -121,7 +122,7 @@ class TimeSeries extends Component { }) try { - const results = await this.executeQueries(queries) + const results = await this.executeQueries(queries, this.props.variables) const tables = flatten(results.map(r => parseResponse(r.csv))) const files = results.map(r => r.csv) diff --git a/ui/src/shared/components/cells/Cell.tsx b/ui/src/shared/components/cells/Cell.tsx index 8e578f7af7..2bee011a87 100644 --- a/ui/src/shared/components/cells/Cell.tsx +++ b/ui/src/shared/components/cells/Cell.tsx @@ -8,6 +8,7 @@ import CellHeader from 'src/shared/components/cells/CellHeader' import CellContext from 'src/shared/components/cells/CellContext' import ViewComponent from 'src/shared/components/cells/View' import {ErrorHandling} from 'src/shared/decorators/errors' +import Conditional from 'src/shared/components/Conditional' // Actions import {readView} from 'src/dashboards/actions/v2/views' @@ -111,19 +112,17 @@ class CellComponent extends Component { onEditCell, } = this.props - if (viewStatus !== RemoteDataState.Done) { - return null - } - return ( - + + + ) } diff --git a/ui/src/shared/utils/queryBuilder.ts b/ui/src/shared/utils/queryBuilder.ts index b93aad7ad9..fb25789588 100644 --- a/ui/src/shared/utils/queryBuilder.ts +++ b/ui/src/shared/utils/queryBuilder.ts @@ -1,4 +1,4 @@ -import {BuilderConfig} from 'src/types/v2' +import {BuilderConfig, TimeRange} from 'src/types/v2' import {FUNCTIONS} from 'src/shared/constants/queryBuilder' const DEFAULT_WINDOW_INTERVAL = '10s' @@ -15,6 +15,18 @@ const WINDOW_INTERVALS = { '30d': '6h', } +export const timeRangeVariables = ( + timeRange: TimeRange +): {[key: string]: string} => { + const result: {[key: string]: string} = {} + + result.timeRangeStart = timeRange.lower + .replace('now()', '') + .replace(/\s/g, '') + + return result +} + export function isConfigValid(builderConfig: BuilderConfig): boolean { const {buckets, measurements} = builderConfig const isConfigValid = buckets.length >= 1 && measurements.length >= 1