Add line specific error text

pull/10616/head
Andrew Watkins 2018-05-16 11:55:56 -07:00
parent 0a0af981d9
commit 206496c656
5 changed files with 68 additions and 3 deletions

View File

@ -9,6 +9,7 @@ import {
OnChangeScript, OnChangeScript,
OnSubmitScript, OnSubmitScript,
FlatBody, FlatBody,
Status,
} from 'src/types/ifql' } from 'src/types/ifql'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
import {HANDLE_VERTICAL, HANDLE_HORIZONTAL} from 'src/shared/constants' import {HANDLE_VERTICAL, HANDLE_HORIZONTAL} from 'src/shared/constants'
@ -17,6 +18,7 @@ interface Props {
data: string data: string
script: string script: string
body: Body[] body: Body[]
status: Status
suggestions: Suggestion[] suggestions: Suggestion[]
onChangeScript: OnChangeScript onChangeScript: OnChangeScript
onSubmitScript: OnSubmitScript onSubmitScript: OnSubmitScript
@ -63,6 +65,7 @@ class TimeMachine extends PureComponent<Props> {
const { const {
body, body,
script, script,
status,
suggestions, suggestions,
onChangeScript, onChangeScript,
onSubmitScript, onSubmitScript,
@ -78,6 +81,7 @@ class TimeMachine extends PureComponent<Props> {
menuOptions: [{action: onSubmitScript, text: 'Analyze'}], menuOptions: [{action: onSubmitScript, text: 'Analyze'}],
render: visibility => ( render: visibility => (
<TimeMachineEditor <TimeMachineEditor
status={status}
script={script} script={script}
onChangeScript={onChangeScript} onChangeScript={onChangeScript}
visibility={visibility} visibility={visibility}

View File

@ -6,10 +6,16 @@ import {ErrorHandling} from 'src/shared/decorators/errors'
import {OnChangeScript} from 'src/types/ifql' import {OnChangeScript} from 'src/types/ifql'
import {editor} from 'src/ifql/constants' import {editor} from 'src/ifql/constants'
interface Status {
type: string
text: string
}
interface Props { interface Props {
script: string script: string
onChangeScript: OnChangeScript
visibility: string visibility: string
status: Status
onChangeScript: OnChangeScript
} }
interface EditorInstance extends IInstance { interface EditorInstance extends IInstance {
@ -19,12 +25,21 @@ interface EditorInstance extends IInstance {
@ErrorHandling @ErrorHandling
class TimeMachineEditor extends PureComponent<Props> { class TimeMachineEditor extends PureComponent<Props> {
private editor: EditorInstance private editor: EditorInstance
private prevKey: string
constructor(props) { constructor(props) {
super(props) super(props)
} }
public componentDidUpdate(prevProps) { public componentDidUpdate(prevProps) {
if (this.props.status.type === 'error') {
this.makeError()
}
if (this.props.status.type !== 'error') {
this.editor.clearGutter('error-gutter')
}
if (prevProps.visibility === this.props.visibility) { if (prevProps.visibility === this.props.visibility) {
return return
} }
@ -45,6 +60,7 @@ class TimeMachineEditor extends PureComponent<Props> {
extraKeys: {'Ctrl-Space': 'autocomplete'}, extraKeys: {'Ctrl-Space': 'autocomplete'},
completeSingle: false, completeSingle: false,
autoRefresh: true, autoRefresh: true,
gutters: ['error-gutter'],
} }
return ( return (
@ -63,6 +79,25 @@ class TimeMachineEditor extends PureComponent<Props> {
) )
} }
private makeError(): void {
const {status} = this.props
this.editor.clearGutter('error-gutter')
const span = document.createElement('span')
span.className = 'icon stop error-warning'
span.title = status.text
const lineNumber = this.statusLine
this.editor.setGutterMarker(lineNumber - 1, 'error-gutter', span)
this.editor.refresh()
}
private get statusLine(): number {
const {status} = this.props
const numbers = status.text.split(' ')[0]
const [lineNumber] = numbers.split(':')
return Number(lineNumber)
}
private handleMount = (instance: EditorInstance) => { private handleMount = (instance: EditorInstance) => {
instance.refresh() // required to for proper line height on mount instance.refresh() // required to for proper line height on mount
this.editor = instance this.editor = instance

View File

@ -13,6 +13,11 @@ import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis'
import * as argTypes from 'src/ifql/constants/argumentTypes' import * as argTypes from 'src/ifql/constants/argumentTypes'
import {ErrorHandling} from 'src/shared/decorators/errors' import {ErrorHandling} from 'src/shared/decorators/errors'
interface Status {
type: string
text: string
}
interface Props { interface Props {
links: Links links: Links
} }
@ -27,6 +32,7 @@ interface State {
script: string script: string
data: string data: string
suggestions: Suggestion[] suggestions: Suggestion[]
status: Status
} }
export const IFQLContext = React.createContext() export const IFQLContext = React.createContext()
@ -42,6 +48,10 @@ export class IFQLPage extends PureComponent<Props, State> {
suggestions: [], suggestions: [],
script: `fil = (r) => r._measurement == "cpu" script: `fil = (r) => r._measurement == "cpu"
tele = from(db: "telegraf") |> filter(fn: fil) |> range(start: -1m) |> sum()`, tele = from(db: "telegraf") |> filter(fn: fil) |> range(start: -1m) |> sum()`,
status: {
type: 'none',
text: '',
},
} }
} }
@ -59,7 +69,7 @@ export class IFQLPage extends PureComponent<Props, State> {
} }
public render() { public render() {
const {suggestions, script, data, body} = this.state const {suggestions, script, data, body, status} = this.state
return ( return (
<IFQLContext.Provider value={this.handlers}> <IFQLContext.Provider value={this.handlers}>
@ -84,6 +94,7 @@ export class IFQLPage extends PureComponent<Props, State> {
data={data} data={data}
body={body} body={body}
script={script} script={script}
status={status}
suggestions={suggestions} suggestions={suggestions}
onChangeScript={this.handleChangeScript} onChangeScript={this.handleChangeScript}
onSubmitScript={this.handleSubmitScript} onSubmitScript={this.handleSubmitScript}
@ -333,8 +344,13 @@ export class IFQLPage extends PureComponent<Props, State> {
try { try {
const ast = await getAST({url: links.ast, body: script}) const ast = await getAST({url: links.ast, body: script})
const body = bodyNodes(ast, this.state.suggestions) const body = bodyNodes(ast, this.state.suggestions)
this.setState({ast, script, body}) const status = {type: 'success', text: ''}
this.setState({ast, script, body, status})
} catch (error) { } catch (error) {
const s = error.data.slice(0, -5) // There is a null newline at the end of these responses
const data = JSON.parse(s)
const status = {type: 'error', text: `${data.message}`}
this.setState({status})
return console.error('Could not parse AST', error) return console.error('Could not parse AST', error)
} }
} }

View File

@ -19,3 +19,8 @@
width: 100%; width: 100%;
height: 100%; height: 100%;
} }
.error-warning {
color: $c-dreamsicle;
cursor: pointer;
}

View File

@ -10,6 +10,11 @@ export type OnGenerateScript = (script: string) => void
export type OnChangeScript = (script: string) => void export type OnChangeScript = (script: string) => void
export type OnSubmitScript = () => void export type OnSubmitScript = () => void
export interface Status {
type: string
text: string
}
export interface Handlers { export interface Handlers {
onAddNode: OnAddNode onAddNode: OnAddNode
onChangeArg: OnChangeArg onChangeArg: OnChangeArg