diff --git a/ui/src/ifql/components/TimeMachine.tsx b/ui/src/ifql/components/TimeMachine.tsx index abcec6685..ecc12012b 100644 --- a/ui/src/ifql/components/TimeMachine.tsx +++ b/ui/src/ifql/components/TimeMachine.tsx @@ -9,6 +9,7 @@ import { OnChangeScript, OnSubmitScript, FlatBody, + Status, } from 'src/types/ifql' import {ErrorHandling} from 'src/shared/decorators/errors' import {HANDLE_VERTICAL, HANDLE_HORIZONTAL} from 'src/shared/constants' @@ -17,6 +18,7 @@ interface Props { data: string script: string body: Body[] + status: Status suggestions: Suggestion[] onChangeScript: OnChangeScript onSubmitScript: OnSubmitScript @@ -63,6 +65,7 @@ class TimeMachine extends PureComponent { const { body, script, + status, suggestions, onChangeScript, onSubmitScript, @@ -78,6 +81,7 @@ class TimeMachine extends PureComponent { menuOptions: [{action: onSubmitScript, text: 'Analyze'}], render: visibility => ( { private editor: EditorInstance + private prevKey: string constructor(props) { super(props) } 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) { return } @@ -45,6 +60,7 @@ class TimeMachineEditor extends PureComponent { extraKeys: {'Ctrl-Space': 'autocomplete'}, completeSingle: false, autoRefresh: true, + gutters: ['error-gutter'], } return ( @@ -63,6 +79,25 @@ class TimeMachineEditor extends PureComponent { ) } + 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) => { instance.refresh() // required to for proper line height on mount this.editor = instance diff --git a/ui/src/ifql/containers/IFQLPage.tsx b/ui/src/ifql/containers/IFQLPage.tsx index 20d179227..8c4719d45 100644 --- a/ui/src/ifql/containers/IFQLPage.tsx +++ b/ui/src/ifql/containers/IFQLPage.tsx @@ -13,6 +13,11 @@ import {getSuggestions, getAST, getTimeSeries} from 'src/ifql/apis' import * as argTypes from 'src/ifql/constants/argumentTypes' import {ErrorHandling} from 'src/shared/decorators/errors' +interface Status { + type: string + text: string +} + interface Props { links: Links } @@ -27,6 +32,7 @@ interface State { script: string data: string suggestions: Suggestion[] + status: Status } export const IFQLContext = React.createContext() @@ -42,6 +48,10 @@ export class IFQLPage extends PureComponent { suggestions: [], script: `fil = (r) => r._measurement == "cpu" tele = from(db: "telegraf") |> filter(fn: fil) |> range(start: -1m) |> sum()`, + status: { + type: 'none', + text: '', + }, } } @@ -59,7 +69,7 @@ export class IFQLPage extends PureComponent { } public render() { - const {suggestions, script, data, body} = this.state + const {suggestions, script, data, body, status} = this.state return ( @@ -84,6 +94,7 @@ export class IFQLPage extends PureComponent { data={data} body={body} script={script} + status={status} suggestions={suggestions} onChangeScript={this.handleChangeScript} onSubmitScript={this.handleSubmitScript} @@ -333,8 +344,13 @@ export class IFQLPage extends PureComponent { try { const ast = await getAST({url: links.ast, body: script}) 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) { + 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) } } diff --git a/ui/src/style/components/time-machine/ifql-editor.scss b/ui/src/style/components/time-machine/ifql-editor.scss index 843646300..0612898d8 100644 --- a/ui/src/style/components/time-machine/ifql-editor.scss +++ b/ui/src/style/components/time-machine/ifql-editor.scss @@ -19,3 +19,8 @@ width: 100%; height: 100%; } + +.error-warning { + color: $c-dreamsicle; + cursor: pointer; +} \ No newline at end of file diff --git a/ui/src/types/ifql.ts b/ui/src/types/ifql.ts index 1792b1744..f90eba4be 100644 --- a/ui/src/types/ifql.ts +++ b/ui/src/types/ifql.ts @@ -10,6 +10,11 @@ export type OnGenerateScript = (script: string) => void export type OnChangeScript = (script: string) => void export type OnSubmitScript = () => void +export interface Status { + type: string + text: string +} + export interface Handlers { onAddNode: OnAddNode onChangeArg: OnChangeArg