Surface errors appearing within Flux tables

pull/12875/head
Christopher Henn 2019-03-21 12:03:35 -07:00 committed by Chris Henn
parent 4565d6b57c
commit 7d7d366684
6 changed files with 97 additions and 2 deletions

View File

@ -9,6 +9,7 @@
1. [12791](https://github.com/influxdata/influxdb/pull/12791): Use time range for metaqueries in Data Explorer and Cell Editor Overlay
1. [12827](https://github.com/influxdata/influxdb/pull/12827): Fix screen tearing bug in Raw Data View
1. [12843](https://github.com/influxdata/influxdb/pull/12843): Add copy to clipboard button to export overlays
1. [12826](https://github.com/influxdata/influxdb/pull/12826): Enable copying error messages to the clipboard from dashboard cells
### Bug Fixes

View File

@ -23,4 +23,9 @@
top: $ix-marg-b;
right: $ix-marg-b;
opacity: 0.9;
display: none;
.empty-graph-error:hover & {
display: inherit;
}
}

View File

@ -12,6 +12,7 @@ import {
// Utils
import {parseResponse} from 'src/shared/parsing/flux/response'
import {getActiveOrg} from 'src/organizations/selectors'
import {checkQueryResult} from 'src/shared/utils/checkQueryResult'
// Types
import {RemoteDataState, FluxTable} from 'src/types'
@ -137,6 +138,8 @@ class TimeSeries extends Component<Props, State> {
const tables = flatten(results.map(r => parseResponse(r.csv)))
const files = results.map(r => r.csv)
files.forEach(checkQueryResult)
this.setState({
tables,
files,

View File

@ -0,0 +1,28 @@
import {checkQueryResult} from 'src/shared/utils/checkQueryResult'
describe('checkQueryResult', () => {
test('throws an error when the response has an error table', () => {
const RESPONSE = `#group,true,true
#datatype,string,string
#default,,
,error,reference
,"function references unknown column ""_value""",`
expect(() => {
checkQueryResult(RESPONSE)
}).toThrow('function references unknown column')
})
test('does not throw an error when the response is valid', () => {
const RESPONSE = `#group,false,false,true,true,false,false,true,true,true
#datatype,string,long,dateTime:RFC3339,dateTime:RFC3339,dateTime:RFC3339,long,string,string,string
#default,_result,,,,,,,,
,result,table,_start,_stop,_time,_value,_measurement,host,_field
,,0,2019-03-21T18:54:14.113478Z,2019-03-21T19:54:14.113478Z,2019-03-21T18:54:21Z,4780101632,mem,oox4k.local,active
,,0,2019-03-21T18:54:14.113478Z,2019-03-21T19:54:14.113478Z,2019-03-21T18:54:31Z,5095436288,mem,oox4k.local,active`
expect(() => {
checkQueryResult(RESPONSE)
}).not.toThrow()
})
})

View File

@ -0,0 +1,56 @@
/*
Given Flux query response as a CSV, check if the CSV contains an error table
as the first result. If it does, throw the error message contained within
that table.
For example, given the following response:
#datatype,string,long
,error,reference
,Failed to parse query,897
we want to throw an error with the message "Failed to parse query".
See https://github.com/influxdata/flux/blob/master/docs/SPEC.md#errors.
*/
export const checkQueryResult = (file: string): void => {
// Don't check the whole file, since it could be huge and the error table
// will be within the first few lines (if it exists)
const fileHead = file.slice(0, findNthIndex(file, '\n', 6))
const lines = fileHead.split('\n').filter(line => !line.startsWith('#'))
if (!lines.length || !lines[0].includes('error') || !lines[1]) {
return
}
const header = lines[0].split(',').map(s => s.trim())
const row = lines[1].split(',').map(s => s.trim())
const index = header.indexOf('error')
if (index === -1 || !row[index]) {
return
}
// Trim off extra quotes at start and end of message
const errorMessage = row[index].replace(/^"/, '').replace(/"$/, '')
throw new Error(errorMessage)
}
const findNthIndex = (s: string, c: string, n: number) => {
let count = 0
let i = 0
while (i < s.length) {
if (s[i] == c) {
count += 1
}
if (count === n) {
return i
}
i += 1
}
}

View File

@ -13,6 +13,7 @@ import {getActiveOrg} from 'src/organizations/selectors'
import {getVariableAssignments} from 'src/variables/selectors'
import {getTimeRangeVars} from 'src/variables/utils/getTimeRangeVars'
import {filterUnusedVars} from 'src/shared/utils/filterUnusedVars'
import {checkQueryResult} from 'src/shared/utils/checkQueryResult'
import {
getVariablesForOrg,
getVariable,
@ -113,9 +114,10 @@ export const executeQueries = () => async (dispatch, getState: GetState) => {
const results = await Promise.all(pendingResults.map(r => r.promise))
const duration = Date.now() - startTime
const files = results.map(r => r.csv)
files.forEach(checkQueryResult)
dispatch(setQueryResults(RemoteDataState.Done, files, duration))
} catch (e) {
if (e instanceof CancellationError) {