diff --git a/ui/package.json b/ui/package.json index 078004f99..45e507c50 100644 --- a/ui/package.json +++ b/ui/package.json @@ -39,7 +39,7 @@ "@types/d3-scale": "^2.0.1", "@types/dygraphs": "^1.1.6", "@types/enzyme": "^3.1.13", - "@types/jest": "^22.1.4", + "@types/jest": "^23.3.5", "@types/levelup": "^0.0.30", "@types/lodash": "^4.14.104", "@types/node": "^9.4.6", diff --git a/ui/src/flux/apis/index.ts b/ui/src/flux/apis/index.ts index 5dad14925..a81dec9c8 100644 --- a/ui/src/flux/apis/index.ts +++ b/ui/src/flux/apis/index.ts @@ -6,7 +6,7 @@ import { } from 'src/shared/parsing/flux/response' import {MAX_RESPONSE_BYTES} from 'src/flux/constants' import {manager} from 'src/worker/JobManager' -import {addTemplatesToScript} from 'src/shared/parsing/flux/templates' +import {renderTemplatesInScript} from 'src/flux/helpers/templates' import _ from 'lodash' export const getSuggestions = async (url: string) => { @@ -58,17 +58,25 @@ export interface GetRawTimeSeriesResult { export const getRawTimeSeries = async ( source: Source, script: string, + uuid: string, timeRange: TimeRange, - uuid?: string + fluxASTLink: string, + maxSideLength: number ): Promise => { const path = encodeURIComponent(`/v2/query?organization=defaultorgname`) const url = `${window.basepath}${source.links.flux}?path=${path}` - const scriptWithTemplates = addTemplatesToScript(script, timeRange) + const renderedScript = await renderTemplatesInScript( + script, + timeRange, + fluxASTLink, + maxSideLength + ) + try { const {body, byteLength, uuid: responseUUID} = await manager.fetchFluxData( url, - scriptWithTemplates, + renderedScript, uuid ) @@ -99,14 +107,18 @@ export interface GetTimeSeriesResult { export const getTimeSeries = async ( source, script: string, + uuid: string, timeRange: TimeRange, - uuid?: string + fluxASTLink: string, + maxSideLength: number ): Promise => { const {csv, didTruncate, uuid: responseUUID} = await getRawTimeSeries( source, script, + uuid, timeRange, - uuid + fluxASTLink, + maxSideLength ) const tables = parseResponse(csv) diff --git a/ui/src/flux/helpers/templates.ts b/ui/src/flux/helpers/templates.ts new file mode 100644 index 000000000..c63ef1292 --- /dev/null +++ b/ui/src/flux/helpers/templates.ts @@ -0,0 +1,52 @@ +import {TimeRange} from 'src/types' +import {getMinDuration} from 'src/shared/parsing/flux/durations' +import { + DEFAULT_PIXELS, + DEFAULT_DURATION_MS, + RESOLUTION_SCALE_FACTOR, +} from 'src/shared/constants' + +// For now we only support these template variables in Flux queries +const DASHBOARD_TIME = 'dashboardTime' +const UPPER_DASHBOARD_TIME = 'upperDashboardTime' +const INTERVAL = 'autoInterval' + +const INTERVAL_REGEX = /autoInterval/g + +export const renderTemplatesInScript = async ( + script: string, + timeRange: TimeRange, + astLink: string, + maxSideLength: number = DEFAULT_PIXELS +): Promise => { + let dashboardTime: string + let upperDashboardTime: string + + if (timeRange.upper) { + dashboardTime = timeRange.lower + upperDashboardTime = timeRange.upper + } else { + dashboardTime = timeRange.lowerFlux || '1h' + upperDashboardTime = new Date().toISOString() + } + + let rendered = `${DASHBOARD_TIME} = ${dashboardTime}\n\n${UPPER_DASHBOARD_TIME} = ${upperDashboardTime}\n\n${script}` + + if (!script.match(INTERVAL_REGEX)) { + return rendered + } + + let duration: number + + try { + duration = await getMinDuration(astLink, rendered) + } catch (error) { + duration = DEFAULT_DURATION_MS + } + + const interval = duration / (maxSideLength * RESOLUTION_SCALE_FACTOR) + + rendered = `${INTERVAL} = ${Math.floor(interval)}ms\n\n${rendered}` + + return rendered +} diff --git a/ui/src/shared/apis/query.ts b/ui/src/shared/apis/query.ts index 42033572a..eb3be89f3 100644 --- a/ui/src/shared/apis/query.ts +++ b/ui/src/shared/apis/query.ts @@ -51,7 +51,7 @@ const replace = async ( const durationMs = await duration(templateReplacedQuery, source) const replacedQuery = replaceInterval( templateReplacedQuery, - Math.floor(resolution / 3), + resolution, durationMs ) diff --git a/ui/src/shared/components/RefreshingGraph.tsx b/ui/src/shared/components/RefreshingGraph.tsx index cd721ba93..5862c59e9 100644 --- a/ui/src/shared/components/RefreshingGraph.tsx +++ b/ui/src/shared/components/RefreshingGraph.tsx @@ -83,6 +83,7 @@ interface Props { autoRefresher: AutoRefresher manualRefresh: number resizerTopHeight: number + fluxASTLink: string onZoom: () => void editQueryStatus: () => void onSetResolution: () => void @@ -121,6 +122,7 @@ class RefreshingGraph extends Component { onNotify, timeRange, templates, + fluxASTLink, grabFluxData, manualRefresh, autoRefresher, @@ -152,6 +154,7 @@ class RefreshingGraph extends Component { queries={this.queries} timeRange={timeRange} templates={templates} + fluxASTLink={fluxASTLink} editQueryStatus={editQueryStatus} onNotify={onNotify} grabDataForDownload={grabDataForDownload} @@ -469,8 +472,9 @@ class RefreshingGraph extends Component { } } -const mapStateToProps = ({annotations: {mode}}) => ({ +const mapStateToProps = ({links, annotations: {mode}}) => ({ mode, + fluxASTLink: links.flux.ast, }) const mdtp = { diff --git a/ui/src/shared/components/TimeMachine/CSVExporter.tsx b/ui/src/shared/components/TimeMachine/CSVExporter.tsx index 90faeb0c3..de28d25e0 100644 --- a/ui/src/shared/components/TimeMachine/CSVExporter.tsx +++ b/ui/src/shared/components/TimeMachine/CSVExporter.tsx @@ -23,6 +23,7 @@ interface Props { script: string source: Source timeRange: TimeRange + fluxASTLink: string isFluxSelected: boolean onNotify: typeof notify @@ -78,9 +79,14 @@ class CSVExporter extends PureComponent { } private downloadFluxCSV = async (): Promise => { - const {source, script, onNotify, timeRange} = this.props + const {source, script, onNotify, timeRange, fluxASTLink} = this.props - const {didTruncate} = await downloadFluxCSV(source, script, timeRange) + const {didTruncate} = await downloadFluxCSV( + source, + script, + timeRange, + fluxASTLink + ) if (didTruncate) { onNotify(fluxResponseTruncatedError()) @@ -90,8 +96,12 @@ class CSVExporter extends PureComponent { } } +const mstp = state => ({ + fluxASTLink: state.links.flux.ast, +}) + const mdtp = { onNotify: notify, } -export default connect(null, mdtp)(CSVExporter) +export default connect(mstp, mdtp)(CSVExporter) diff --git a/ui/src/shared/components/time_series/TimeSeries.tsx b/ui/src/shared/components/time_series/TimeSeries.tsx index 1fffc8716..8df1342fb 100644 --- a/ui/src/shared/components/time_series/TimeSeries.tsx +++ b/ui/src/shared/components/time_series/TimeSeries.tsx @@ -50,6 +50,7 @@ interface Props { children: (r: RenderProps) => JSX.Element inView?: boolean templates?: Template[] + fluxASTLink?: string editQueryStatus?: (queryID: string, status: Status) => void grabDataForDownload?: GrabDataForDownloadHandler grabFluxData?: (data: FluxTable[]) => void @@ -66,6 +67,8 @@ interface State { latestUUID: string } +const TEMP_RES = 300 // FIXME + const GraphLoadingDots = () => (
@@ -255,14 +258,16 @@ class TimeSeries extends PureComponent { private executeFluxQuery = async ( latestUUID: string ): Promise => { - const {queries, onNotify, source, timeRange} = this.props + const {queries, onNotify, source, timeRange, fluxASTLink} = this.props const script: string = _.get(queries, '0.text', '') const results = await fetchFluxTimeSeries( source, script, + latestUUID, timeRange, - latestUUID + fluxASTLink, + TEMP_RES ) if (results.didTruncate && onNotify) { @@ -288,7 +293,6 @@ class TimeSeries extends PureComponent { latestUUID: string ): Promise => { const {source, templates, editQueryStatus} = this.props - const TEMP_RES = 300 // FIXME editQueryStatus(query.id, {loading: true}) diff --git a/ui/src/shared/constants/index.ts b/ui/src/shared/constants/index.ts index 9fc5415fa..ecf584eac 100644 --- a/ui/src/shared/constants/index.ts +++ b/ui/src/shared/constants/index.ts @@ -8,6 +8,7 @@ export const GIT_SHA = process.env.GIT_SHA export const DEFAULT_DURATION_MS = 1000 export const DEFAULT_PIXELS = 333 +export const RESOLUTION_SCALE_FACTOR = 0.5 export const NO_CELL = 'none' diff --git a/ui/src/shared/parsing/flux/durations.ts b/ui/src/shared/parsing/flux/durations.ts new file mode 100644 index 000000000..9f0aa6e8c --- /dev/null +++ b/ui/src/shared/parsing/flux/durations.ts @@ -0,0 +1,253 @@ +import {get, isObject, isArray} from 'lodash' +import {getAST} from 'src/flux/apis' + +export async function getMinDuration( + astLink: string, + fluxQuery: string +): Promise { + const ast = await getAST({url: astLink, body: fluxQuery}) + const result = getMinDurationFromAST(ast) + + return result +} + +export function getMinDurationFromAST(ast: any) { + // We can't take the minimum of durations of each range individually, since + // seperate ranges are potentially combined via an inner `join` call. So the + // approach is to: + // + // 1. Find every possible `[start, stop]` combination for all start and stop + // times across every `range` call + // 2. Map each combination to a duration via `stop - start` + // 3. Filter out the non-positive durations + // 4. Take the minimum duration + // + const times = allRangeTimes(ast) + const starts = times.map(t => t[0]) + const stops = times.map(t => t[1]) + const crossProduct = starts.map(start => stops.map(stop => [start, stop])) + + const durations = [] + .concat(...crossProduct) + .map(([start, stop]) => stop - start) + .filter(d => d > 0) + + const result = Math.min(...durations) + + return result +} + +// The following interfaces only represent AST structs as they appear +// in the context of a `range` call + +interface RangeCallExpression { + type: 'CallExpression' + callee: { + type: 'Identifier' + name: 'range' + } + arguments: [{properties: RangeCallProperty[]}] +} + +interface RangeCallProperty { + type: 'Property' + key: { + name: 'start' | 'stop' + } + value: RangeCallPropertyValue +} + +type RangeCallPropertyValue = + | MinusUnaryExpression + | DurationLiteral + | DateTimeLiteral + | Identifier + | DurationBinaryExpression + +interface MinusUnaryExpression { + type: 'UnaryExpression' + operator: '-' + argument: T +} + +interface DurationLiteral { + type: 'DurationLiteral' + values: Array<{ + magnitude: number + unit: DurationUnit + }> +} + +type DurationUnit = + | 'y' + | 'mo' + | 'w' + | 'd' + | 'h' + | 'm' + | 's' + | 'ms' + | 'us' + | 'µs' + | 'ns' + +interface DateTimeLiteral { + type: 'DateTimeLiteral' + value: string +} + +interface Identifier { + type: 'Identifier' + name: string +} + +interface DurationBinaryExpression { + type: 'BinaryExpression' + left: DateTimeLiteral + right: DurationLiteral + operator: '+' | '-' +} + +export function allRangeTimes(ast: any): Array<[number, number]> { + return findNodes(isRangeNode, ast).map(node => rangeTimes(ast, node)) +} + +/* + Given a `range` call in an AST, reports the `start` and `stop` arguments the + the call as absolute instants in time. If the `start` or `stop` argument is a + relative duration literal, it is interpreted as relative to the current + instant (`Date.now()`). +*/ +function rangeTimes( + ast: any, + rangeNode: RangeCallExpression +): [number, number] { + const properties = rangeNode.arguments[0].properties + const now = Date.now() + + // The `start` argument is required + const startProperty = properties.find(p => p.key.name === 'start') + const start = propertyTime(ast, startProperty.value, now) + + // The `end` argument to a `range` call is optional, and defaults to now + const endProperty = properties.find(p => p.key.name === 'stop') + const end = endProperty ? propertyTime(ast, endProperty.value, now) : now + + if (isNaN(start) || isNaN(end)) { + throw new Error('failed to analyze query') + } + + return [start, end] +} + +function propertyTime( + ast: any, + value: RangeCallPropertyValue, + now: number +): number { + switch (value.type) { + case 'UnaryExpression': + return now - durationDuration(value.argument) + case 'DurationLiteral': + return now + durationDuration(value) + case 'DateTimeLiteral': + return Date.parse(value.value) + case 'Identifier': + return propertyTime(ast, resolveDeclaration(ast, value.name), now) + case 'BinaryExpression': + const leftTime = Date.parse(value.left.value) + const rightDuration = durationDuration(value.right) + + switch (value.operator) { + case '+': + return leftTime + rightDuration + case '-': + return leftTime - rightDuration + } + } +} + +const UNIT_TO_APPROX_DURATION = { + ns: 1 / 1000000, + µs: 1 / 1000, + us: 1 / 1000, + ms: 1, + s: 1000, + m: 1000 * 60, + h: 1000 * 60 * 60, + d: 1000 * 60 * 60 * 24, + w: 1000 * 60 * 60 * 24 * 7, + mo: 1000 * 60 * 60 * 24 * 30, + y: 1000 * 60 * 60 * 24 * 365, +} + +function durationDuration(durationLiteral: DurationLiteral): number { + const duration = durationLiteral.values.reduce( + (sum, {magnitude, unit}) => sum + magnitude * UNIT_TO_APPROX_DURATION[unit], + 0 + ) + + return duration +} + +/* + Find the node in the `ast` that defines the value of the variable with the + given `name`. +*/ +function resolveDeclaration(ast: any, name: string): RangeCallPropertyValue { + const isDeclarator = node => { + return ( + get(node, 'type') === 'VariableDeclarator' && + get(node, 'id.name') === name + ) + } + + const declarator = findNodes(isDeclarator, ast) + + if (!declarator.length) { + throw new Error(`unable to resolve identifier "${name}"`) + } + + if (declarator.length > 1) { + throw new Error('cannot resolve identifier with duplicate declarations') + } + + const init = declarator[0].init + + return init +} + +function isRangeNode(node: any) { + return ( + get(node, 'type') === 'CallExpression' && + get(node, 'callee.type') === 'Identifier' && + get(node, 'callee.name') === 'range' + ) +} + +/* + Find all nodes in a tree matching the `predicate` function. Each node in the + tree is an object, which may contain objects or arrays of objects as children + under any key. +*/ +function findNodes( + predicate: (node: any[]) => boolean, + node: any, + acc: any[] = [] +) { + if (predicate(node)) { + acc.push(node) + } + + for (const value of Object.values(node)) { + if (isObject(value)) { + findNodes(predicate, value, acc) + } else if (isArray(value)) { + for (const innerValue of value) { + findNodes(predicate, innerValue, acc) + } + } + } + + return acc +} diff --git a/ui/src/shared/utils/downloadTimeseriesCSV.ts b/ui/src/shared/utils/downloadTimeseriesCSV.ts index 9566fb728..97c3c0670 100644 --- a/ui/src/shared/utils/downloadTimeseriesCSV.ts +++ b/ui/src/shared/utils/downloadTimeseriesCSV.ts @@ -1,12 +1,16 @@ // Libraries import moment from 'moment' import {unparse} from 'papaparse' +import uuid from 'uuid' // Utils import {timeSeriesToTableGraph} from 'src/utils/timeSeriesTransformers' import {executeQuery as executeInfluxQLQuery} from 'src/shared/apis/query' import {getRawTimeSeries as executeFluxQuery} from 'src/flux/apis/index' +// Constants +import {DEFAULT_PIXELS} from 'src/shared/constants' + // Types import {Query, Template, Source, TimeRange} from 'src/types' import {TimeSeriesResponse} from 'src/types/series' @@ -29,9 +33,17 @@ export const downloadInfluxQLCSV = async ( export const downloadFluxCSV = async ( source: Source, script: string, - timeRange: TimeRange + timeRange: TimeRange, + fluxASTLink: string ): Promise<{didTruncate: boolean}> => { - const {csv, didTruncate} = await executeFluxQuery(source, script, timeRange) + const {csv, didTruncate} = await executeFluxQuery( + source, + script, + uuid.v4(), + timeRange, + fluxASTLink, + DEFAULT_PIXELS + ) downloadCSV(csv, csvName()) diff --git a/ui/src/tempVars/utils/replace.ts b/ui/src/tempVars/utils/replace.ts index 1283a5399..d4f16b209 100644 --- a/ui/src/tempVars/utils/replace.ts +++ b/ui/src/tempVars/utils/replace.ts @@ -10,6 +10,7 @@ import { TEMP_VAR_INTERVAL, DEFAULT_PIXELS, DEFAULT_DURATION_MS, + RESOLUTION_SCALE_FACTOR, } from 'src/shared/constants' function sortTemplates(templates: Template[]): Template[] { @@ -35,8 +36,7 @@ export const replaceInterval = ( durationMs = DEFAULT_DURATION_MS } - // duration / width of visualization in pixels - const msPerPixel = Math.floor(durationMs / pixels) + const msPerPixel = Math.floor(durationMs / (pixels * RESOLUTION_SCALE_FACTOR)) return replaceAll(query, TEMP_VAR_INTERVAL, `${msPerPixel}ms`) } diff --git a/ui/test/shared/parsing/flux/durations.test.ts b/ui/test/shared/parsing/flux/durations.test.ts new file mode 100644 index 000000000..e38f96e4c --- /dev/null +++ b/ui/test/shared/parsing/flux/durations.test.ts @@ -0,0 +1,8 @@ +import {getMinDurationFromAST} from 'src/shared/parsing/flux/durations' +import {AST_TESTS} from 'test/shared/parsing/flux/durations' + +describe('getMinDurationFromAST', () => { + test.each(AST_TESTS)('%s:\n\n```\n%s\n```', (__, ___, expected, ast) => { + expect(getMinDurationFromAST(ast)).toEqual(expected) + }) +}) diff --git a/ui/test/shared/parsing/flux/durations.ts b/ui/test/shared/parsing/flux/durations.ts new file mode 100644 index 000000000..cf0a0b1d0 --- /dev/null +++ b/ui/test/shared/parsing/flux/durations.ts @@ -0,0 +1,1766 @@ +const AST_1 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 39}, + source: 'from(bucket: "b") |\u003e range(start: -1m)', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 39}, + source: 'from(bucket: "b") |\u003e range(start: -1m)', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 39}, + source: '|\u003e range(start: -1m)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 39}, + source: 'range(start: -1m)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 38}, + source: 'start: -1m', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 38}, + source: 'start: -1m', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 38}, + source: '-1m', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 36}, + end: {line: 1, column: 38}, + source: '1m', + }, + values: [{magnitude: 1, unit: 'm'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_2 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 50}, + source: 'foo = -1m\n\nfrom(bucket: "b") |\u003e range(start: foo)', + }, + body: [ + { + type: 'VariableDeclaration', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 12}, + source: 'foo = -1m\n\n', + }, + declarations: [ + { + type: 'VariableDeclarator', + id: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 4}, + source: 'foo', + }, + name: 'foo', + }, + init: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 7}, + end: {line: 1, column: 12}, + source: '-1m\n\n', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 8}, + end: {line: 1, column: 10}, + source: '1m', + }, + values: [{magnitude: 1, unit: 'm'}], + }, + }, + }, + ], + }, + { + type: 'ExpressionStatement', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 39}, + source: 'from(bucket: "b") |\u003e range(start: foo)', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 3, column: 19}, + end: {line: 3, column: 39}, + source: '|\u003e range(start: foo)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 3, column: 6}, + end: {line: 3, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 3, column: 6}, + end: {line: 3, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 3, column: 6}, + end: {line: 3, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 3, column: 14}, + end: {line: 3, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 3, column: 22}, + end: {line: 3, column: 39}, + source: 'range(start: foo)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 3, column: 22}, + end: {line: 3, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 3, column: 28}, + end: {line: 3, column: 38}, + source: 'start: foo', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 3, column: 28}, + end: {line: 3, column: 38}, + source: 'start: foo', + }, + key: { + type: 'Identifier', + location: { + start: {line: 3, column: 28}, + end: {line: 3, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'Identifier', + location: { + start: {line: 3, column: 35}, + end: {line: 3, column: 38}, + source: 'foo', + }, + name: 'foo', + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_3 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 50}, + source: 'from(bucket: "b") |\u003e range(start: -1h, stop: -3m)', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 50}, + source: 'from(bucket: "b") |\u003e range(start: -1h, stop: -3m)', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 50}, + source: '|\u003e range(start: -1h, stop: -3m)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 50}, + source: 'range(start: -1h, stop: -3m)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 49}, + source: 'start: -1h, stop: -3m', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 38}, + source: 'start: -1h', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 38}, + source: '-1h', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 36}, + end: {line: 1, column: 38}, + source: '1h', + }, + values: [{magnitude: 1, unit: 'h'}], + }, + }, + }, + { + type: 'Property', + location: { + start: {line: 1, column: 40}, + end: {line: 1, column: 49}, + source: 'stop: -3m', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 40}, + end: {line: 1, column: 44}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 46}, + end: {line: 1, column: 49}, + source: '-3m', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 47}, + end: {line: 1, column: 49}, + source: '3m', + }, + values: [{magnitude: 3, unit: 'm'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_4 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 85}, + source: + 'from(bucket: "b") |\u003e range(start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z) ', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 85}, + source: + 'from(bucket: "b") |\u003e range(start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z) ', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 84}, + source: + '|\u003e range(start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 84}, + source: + 'range(start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 83}, + source: + 'start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 55}, + source: 'start: 2010-01-01T00:00:00Z', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'DateTimeLiteral', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 55}, + source: '2010-01-01T00:00:00Z', + }, + value: '2010-01-01T00:00:00Z', + }, + }, + { + type: 'Property', + location: { + start: {line: 1, column: 57}, + end: {line: 1, column: 83}, + source: 'stop: 2010-01-02T00:00:00Z', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 57}, + end: {line: 1, column: 61}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'DateTimeLiteral', + location: { + start: {line: 1, column: 63}, + end: {line: 1, column: 83}, + source: '2010-01-02T00:00:00Z', + }, + value: '2010-01-02T00:00:00Z', + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_5 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 91}, + source: + 'from(bucket: "b") |\u003e range(start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z) ', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 91}, + source: + 'from(bucket: "b") |\u003e range(start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z) ', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 89}, + source: + '|\u003e range(start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 89}, + source: + 'range(start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 88}, + source: + 'start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 60}, + source: 'start: 2010-01-01T00:00:00Z + 1h', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'BinaryExpression', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 60}, + source: '2010-01-01T00:00:00Z + 1h', + }, + operator: '+', + left: { + type: 'DateTimeLiteral', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 55}, + source: '2010-01-01T00:00:00Z', + }, + value: '2010-01-01T00:00:00Z', + }, + right: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 58}, + end: {line: 1, column: 60}, + source: '1h', + }, + values: [{magnitude: 1, unit: 'h'}], + }, + }, + }, + { + type: 'Property', + location: { + start: {line: 1, column: 62}, + end: {line: 1, column: 88}, + source: 'stop: 2010-01-02T00:00:00Z', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 62}, + end: {line: 1, column: 66}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'DateTimeLiteral', + location: { + start: {line: 1, column: 68}, + end: {line: 1, column: 88}, + source: '2010-01-02T00:00:00Z', + }, + value: '2010-01-02T00:00:00Z', + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_6 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 78}, + source: + 'from(bucket: "b") |\u003e range(start: -2m)\nfrom(bucket: "b") |\u003e range(start: -1m)', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 40}, + source: 'from(bucket: "b") |\u003e range(start: -2m)\n', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 39}, + source: '|\u003e range(start: -2m)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 39}, + source: 'range(start: -2m)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 38}, + source: 'start: -2m', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 38}, + source: 'start: -2m', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 38}, + source: '-2m', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 36}, + end: {line: 1, column: 38}, + source: '2m', + }, + values: [{magnitude: 2, unit: 'm'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + { + type: 'ExpressionStatement', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 39}, + source: 'from(bucket: "b") |\u003e range(start: -1m)', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 2, column: 19}, + end: {line: 2, column: 39}, + source: '|\u003e range(start: -1m)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 2, column: 14}, + end: {line: 2, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 2, column: 22}, + end: {line: 2, column: 39}, + source: 'range(start: -1m)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 2, column: 22}, + end: {line: 2, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 38}, + source: 'start: -1m', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 38}, + source: 'start: -1m', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 2, column: 35}, + end: {line: 2, column: 38}, + source: '-1m', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 2, column: 36}, + end: {line: 2, column: 38}, + source: '1m', + }, + values: [{magnitude: 1, unit: 'm'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_7 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 96}, + source: + 'from(bucket: "b") |\u003e range(start: -200d, stop: -100d)\nfrom(bucket: "b") |\u003e range(start: -101d) ', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 55}, + source: 'from(bucket: "b") |\u003e range(start: -200d, stop: -100d)\n', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 54}, + source: '|\u003e range(start: -200d, stop: -100d)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 6}, + end: {line: 1, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 54}, + source: 'range(start: -200d, stop: -100d)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 22}, + end: {line: 1, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 53}, + source: 'start: -200d, stop: -100d', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 40}, + source: 'start: -200d', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 28}, + end: {line: 1, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 35}, + end: {line: 1, column: 40}, + source: '-200d', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 36}, + end: {line: 1, column: 40}, + source: '200d', + }, + values: [{magnitude: 200, unit: 'd'}], + }, + }, + }, + { + type: 'Property', + location: { + start: {line: 1, column: 42}, + end: {line: 1, column: 53}, + source: 'stop: -100d', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 42}, + end: {line: 1, column: 46}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 48}, + end: {line: 1, column: 53}, + source: '-100d', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 49}, + end: {line: 1, column: 53}, + source: '100d', + }, + values: [{magnitude: 100, unit: 'd'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + { + type: 'ExpressionStatement', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 42}, + source: 'from(bucket: "b") |\u003e range(start: -101d) ', + }, + expression: { + type: 'PipeExpression', + location: { + start: {line: 2, column: 19}, + end: {line: 2, column: 41}, + source: '|\u003e range(start: -101d)', + }, + argument: { + type: 'CallExpression', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 18}, + source: 'from(bucket: "b")', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 5}, + source: 'from', + }, + name: 'from', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 17}, + source: 'bucket: "b"', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 17}, + source: 'bucket: "b"', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 6}, + end: {line: 2, column: 12}, + source: 'bucket', + }, + name: 'bucket', + }, + value: { + type: 'StringLiteral', + location: { + start: {line: 2, column: 14}, + end: {line: 2, column: 17}, + source: '"b"', + }, + value: 'b', + }, + }, + ], + }, + ], + }, + call: { + type: 'CallExpression', + location: { + start: {line: 2, column: 22}, + end: {line: 2, column: 41}, + source: 'range(start: -101d)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 2, column: 22}, + end: {line: 2, column: 27}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 40}, + source: 'start: -101d', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 40}, + source: 'start: -101d', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 28}, + end: {line: 2, column: 33}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 2, column: 35}, + end: {line: 2, column: 40}, + source: '-101d', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 2, column: 36}, + end: {line: 2, column: 40}, + source: '101d', + }, + values: [{magnitude: 101, unit: 'd'}], + }, + }, + }, + ], + }, + ], + }, + }, + }, + ], +} + +const AST_8 = { + type: 'Program', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 78}, + source: + 'range(start: -3s, stop: -2s)\nrange(start: -2s, stop: -1s)\nrange(start: -1s) ', + }, + body: [ + { + type: 'ExpressionStatement', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 29}, + source: 'range(start: -3s, stop: -2s)', + }, + expression: { + type: 'CallExpression', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 29}, + source: 'range(start: -3s, stop: -2s)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 1, column: 1}, + end: {line: 1, column: 6}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 1, column: 7}, + end: {line: 1, column: 28}, + source: 'start: -3s, stop: -2s', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 1, column: 7}, + end: {line: 1, column: 17}, + source: 'start: -3s', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 7}, + end: {line: 1, column: 12}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 14}, + end: {line: 1, column: 17}, + source: '-3s', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 15}, + end: {line: 1, column: 17}, + source: '3s', + }, + values: [{magnitude: 3, unit: 's'}], + }, + }, + }, + { + type: 'Property', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 28}, + source: 'stop: -2s', + }, + key: { + type: 'Identifier', + location: { + start: {line: 1, column: 19}, + end: {line: 1, column: 23}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 1, column: 25}, + end: {line: 1, column: 28}, + source: '-2s', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 1, column: 26}, + end: {line: 1, column: 28}, + source: '2s', + }, + values: [{magnitude: 2, unit: 's'}], + }, + }, + }, + ], + }, + ], + }, + }, + { + type: 'ExpressionStatement', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 29}, + source: 'range(start: -2s, stop: -1s)', + }, + expression: { + type: 'CallExpression', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 29}, + source: 'range(start: -2s, stop: -1s)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 2, column: 1}, + end: {line: 2, column: 6}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 2, column: 7}, + end: {line: 2, column: 28}, + source: 'start: -2s, stop: -1s', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 2, column: 7}, + end: {line: 2, column: 17}, + source: 'start: -2s', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 7}, + end: {line: 2, column: 12}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 2, column: 14}, + end: {line: 2, column: 17}, + source: '-2s', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 2, column: 15}, + end: {line: 2, column: 17}, + source: '2s', + }, + values: [{magnitude: 2, unit: 's'}], + }, + }, + }, + { + type: 'Property', + location: { + start: {line: 2, column: 19}, + end: {line: 2, column: 28}, + source: 'stop: -1s', + }, + key: { + type: 'Identifier', + location: { + start: {line: 2, column: 19}, + end: {line: 2, column: 23}, + source: 'stop', + }, + name: 'stop', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 2, column: 25}, + end: {line: 2, column: 28}, + source: '-1s', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 2, column: 26}, + end: {line: 2, column: 28}, + source: '1s', + }, + values: [{magnitude: 1, unit: 's'}], + }, + }, + }, + ], + }, + ], + }, + }, + { + type: 'ExpressionStatement', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 18}, + source: 'range(start: -1s)', + }, + expression: { + type: 'CallExpression', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 18}, + source: 'range(start: -1s)', + }, + callee: { + type: 'Identifier', + location: { + start: {line: 3, column: 1}, + end: {line: 3, column: 6}, + source: 'range', + }, + name: 'range', + }, + arguments: [ + { + type: 'ObjectExpression', + location: { + start: {line: 3, column: 7}, + end: {line: 3, column: 17}, + source: 'start: -1s', + }, + properties: [ + { + type: 'Property', + location: { + start: {line: 3, column: 7}, + end: {line: 3, column: 17}, + source: 'start: -1s', + }, + key: { + type: 'Identifier', + location: { + start: {line: 3, column: 7}, + end: {line: 3, column: 12}, + source: 'start', + }, + name: 'start', + }, + value: { + type: 'UnaryExpression', + location: { + start: {line: 3, column: 14}, + end: {line: 3, column: 17}, + source: '-1s', + }, + operator: '-', + argument: { + type: 'DurationLiteral', + location: { + start: {line: 3, column: 15}, + end: {line: 3, column: 17}, + source: '1s', + }, + values: [{magnitude: 1, unit: 's'}], + }, + }, + }, + ], + }, + ], + }, + }, + ], +} + +// prettier-ignore +export const AST_TESTS = [ + ['basic relative query' , 'from(bucket: "b") |> range(start: -1m)' , 1000 * 60 , AST_1], + ['query with start time assigned to variable' , 'foo = -1m\n\nfrom(bucket: "b") |> range(start: foo)' , 1000 * 60 , AST_2], + ['query with relative start and stop times' , 'from(bucket: "b") |> range(start: -1h, stop: -3m)' , 1000 * 60 * 57 , AST_3], + ['query with absolute start and stop times' , 'from(bucket: "b") |> range(start: 2010-01-01T00:00:00Z, stop: 2010-01-02T00:00:00Z)' , 1000 * 60 * 60 * 24 , AST_4], + ['query with duration literal added to absolute time' , 'from(bucket: "b") |> range(start: 2010-01-01T00:00:00Z + 1h, stop: 2010-01-02T00:00:00Z)' , 1000 * 60 * 60 * 23 , AST_5], + ['query with two range calls' , 'from(bucket: "b") |> range(start: -1m)\nfrom(bucket: "b") |> range(start: -2m) ' , 1000 * 60 , AST_6], + ['query with two large ranges overlapping slightly' , 'from(bucket: "b") |> range(start: -200d, stop: -100d)\nfrom(bucket: "b") |> range(start: -101d)' , 1000 * 60 * 60 * 24 , AST_7], + ['query with three nonoverlapping ranges' , 'range(start: -3s, stop: -2s)\nrange(start: -2s, stop: -1s)\nrange(start: -1s)' , 1000 , AST_8] +] diff --git a/ui/test/tempVars/utils/replace.test.ts b/ui/test/tempVars/utils/replace.test.ts index abe4c8da9..1ba3b586e 100644 --- a/ui/test/tempVars/utils/replace.test.ts +++ b/ui/test/tempVars/utils/replace.test.ts @@ -288,7 +288,7 @@ describe('templates.utils.replace', () => { describe('replaceInterval', () => { it('can replace :interval:', () => { const query = `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(:interval:)` - const expected = `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(46702702ms)` + const expected = `SELECT mean(usage_idle) from "cpu" where time > now() - 4320h group by time(93405405ms)` const pixels = 333 const durationMs = 15551999999 const actual = replaceInterval(query, pixels, durationMs) @@ -298,7 +298,7 @@ describe('templates.utils.replace', () => { it('can replace multiple intervals', () => { const query = `SELECT NON_NEGATIVE_DERIVATIVE(mean(usage_idle), :interval:) from "cpu" where time > now() - 4320h group by time(:interval:)` - const expected = `SELECT NON_NEGATIVE_DERIVATIVE(mean(usage_idle), 46702702ms) from "cpu" where time > now() - 4320h group by time(46702702ms)` + const expected = `SELECT NON_NEGATIVE_DERIVATIVE(mean(usage_idle), 93405405ms) from "cpu" where time > now() - 4320h group by time(93405405ms)` const pixels = 333 const durationMs = 15551999999 @@ -338,7 +338,7 @@ describe('templates.utils.replace', () => { const query = `SELECT mean(usage_idle) from "cpu" WHERE time > :dashboardTime: group by time(:interval:)` let actual = templateReplace(query, vars) actual = replaceInterval(actual, pixels, durationMs) - const expected = `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 24h group by time(259459ms)` + const expected = `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 24h group by time(518918ms)` expect(actual).toBe(expected) }) @@ -373,7 +373,7 @@ describe('templates.utils.replace', () => { const query = `SELECT mean(usage_idle) from "cpu" WHERE time > :dashboardTime: group by time(:interval:)` let actual = templateReplace(query, vars) actual = replaceInterval(actual, pixels, durationMs) - const expected = `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 1h group by time(94736ms)` + const expected = `SELECT mean(usage_idle) from "cpu" WHERE time > now() - 1h group by time(189473ms)` expect(actual).toBe(expected) }) diff --git a/ui/yarn.lock b/ui/yarn.lock index 848371b8a..555267f94 100644 --- a/ui/yarn.lock +++ b/ui/yarn.lock @@ -794,10 +794,10 @@ resolved "https://registry.yarnpkg.com/@types/history/-/history-3.2.2.tgz#b6affa240cb10b5f841c6443d8a24d7f3fc8bb0c" integrity sha512-DMvBzeA2dp1uZZftXkoqPC4TrdHlyuuTabCOxHY6EAKOJRMaPVu8b6lvX0QxEGKZq3cK/h3JCSxgfKmbDOYmRw== -"@types/jest@^22.1.4": - version "22.2.3" - resolved "https://registry.yarnpkg.com/@types/jest/-/jest-22.2.3.tgz#0157c0316dc3722c43a7b71de3fdf3acbccef10d" - integrity sha512-e74sM9W/4qqWB6D4TWV9FQk0WoHtX1X4FJpbjxucMSVJHtFjbQOH3H6yp+xno4br0AKG0wz/kPtaN599GUOvAg== +"@types/jest@^23.3.5": + version "23.3.5" + resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.5.tgz#870a1434208b60603745bfd214fc3fc675142364" + integrity sha512-3LI+vUC3Wju28vbjIjsTKakhMB8HC4l+tMz+Z8WRzVK+kmvezE5jcOvKtBpznWSI5KDLFo+FouUhpTKoekadCA== "@types/levelup@^0.0.30": version "0.0.30"