Surface errors appearing within Flux tables
parent
4565d6b57c
commit
7d7d366684
|
@ -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
|
||||
|
||||
|
|
|
@ -23,4 +23,9 @@
|
|||
top: $ix-marg-b;
|
||||
right: $ix-marg-b;
|
||||
opacity: 0.9;
|
||||
}
|
||||
display: none;
|
||||
|
||||
.empty-graph-error:hover & {
|
||||
display: inherit;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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()
|
||||
})
|
||||
})
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue