diff --git a/package.json b/package.json index fdbf162f159..05481e3e04b 100644 --- a/package.json +++ b/package.json @@ -248,7 +248,7 @@ "@grafana/flamegraph": "workspace:*", "@grafana/google-sdk": "0.1.1", "@grafana/lezer-logql": "0.2.0", - "@grafana/lezer-traceql": "0.0.6", + "@grafana/lezer-traceql": "0.0.7", "@grafana/monaco-logql": "^0.0.7", "@grafana/runtime": "workspace:*", "@grafana/scenes": "^1.15.0", diff --git a/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.test.tsx b/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.test.tsx index 1a99d4ed309..79c1e3a5de0 100644 --- a/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.test.tsx +++ b/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.test.tsx @@ -1,6 +1,6 @@ import { computeErrorMessage, getErrorNodes } from './errorHighlighting'; -describe('computeErrorMarkers', () => { +describe('Check for syntax errors in query', () => { it.each([ ['{span.http.status_code = }', 'Invalid value after comparison or aritmetic operator.'], ['{span.http.status_code 200}', 'Invalid comparison operator after field expression.'], @@ -59,8 +59,20 @@ describe('computeErrorMarkers', () => { ['{.foo=300} && {.foo=300} | avg(.value) =', 'Invalid value after comparison operator.'], ['{.foo=300} | max(duration) > 1hs', 'Invalid value after comparison operator.'], ['{ span.http.status_code', 'Invalid comparison operator after field expression.'], + ['abcxyz', 'Invalid query.'], ])('error message for invalid query - %s, %s', (query: string, expectedErrorMessage: string) => { const errorNode = getErrorNodes(query)[0]; expect(computeErrorMessage(errorNode)).toBe(expectedErrorMessage); }); + + it.each([ + ['123'], + ['abc'], + ['1a2b3c'], + ['{span.status = $code}'], + ['{span.${attribute} = "GET"}'], + ['{span.${attribute:format} = ${value:format} }'], + ])('valid query - %s', (query: string) => { + expect(getErrorNodes(query)).toStrictEqual([]); + }); }); diff --git a/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.tsx b/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.tsx index 42d83347505..05cb706ca9a 100644 --- a/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.tsx +++ b/public/app/plugins/datasource/tempo/traceql/TraceQLEditor.tsx @@ -67,10 +67,18 @@ export function TraceQLEditor(props: Props) { } setupAutoSize(editor); + // Parse query that might already exist (e.g., after a page refresh) + const model = editor.getModel(); + if (model) { + const errorNodes = getErrorNodes(model.getValue()); + setErrorMarkers(monaco, model, errorNodes); + } + // Register callback for query changes editor.onDidChangeModelContent((changeEvent) => { const model = editor.getModel(); - if (!model || model.getValue() === '') { + + if (!model) { return; } diff --git a/public/app/plugins/datasource/tempo/traceql/errorHighlighting.ts b/public/app/plugins/datasource/tempo/traceql/errorHighlighting.ts index dff1c7f9715..bd49a30b80c 100644 --- a/public/app/plugins/datasource/tempo/traceql/errorHighlighting.ts +++ b/public/app/plugins/datasource/tempo/traceql/errorHighlighting.ts @@ -78,6 +78,17 @@ export const computeErrorMessage = (errorNode: SyntaxNode) => { * @returns the error nodes */ export const getErrorNodes = (query: string): SyntaxNode[] => { + // Return immediately if the query is empty, to avoid raising exceptions in processing it + if (query.trim() === '') { + return []; + } + + // Check whether this is a trace ID or traceQL query by checking if it only contains hex characters + const hexOnlyRegex = /^[0-9A-Fa-f]*$/; + if (query.trim().match(hexOnlyRegex)) { + return []; + } + const tree = parser.parse(query); // Find all error nodes and compute the associated erro boundaries diff --git a/yarn.lock b/yarn.lock index 0d9cdf2b00a..0b368148317 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3281,12 +3281,12 @@ __metadata: languageName: node linkType: hard -"@grafana/lezer-traceql@npm:0.0.6": - version: 0.0.6 - resolution: "@grafana/lezer-traceql@npm:0.0.6" +"@grafana/lezer-traceql@npm:0.0.7": + version: 0.0.7 + resolution: "@grafana/lezer-traceql@npm:0.0.7" peerDependencies: "@lezer/lr": ^1.3.0 - checksum: 166a30c38f4f78e1768e80f724176b71becf3fa51414bc8bea4c2c5288a92d1f8b7872280a06c0d3f7f7608b174e5fc930e1af39339b2c2fdf627fb7ac14c6b4 + checksum: 920f8116d61907e12ca1dd51949242abf435e675105699c7b2fcd8c024999ed0ddfd3500b1802111f3ed9ad37e16f40826790658bdf94224cd5b615e20360ab9 languageName: node linkType: hard @@ -17635,7 +17635,7 @@ __metadata: "@grafana/flamegraph": "workspace:*" "@grafana/google-sdk": 0.1.1 "@grafana/lezer-logql": 0.2.0 - "@grafana/lezer-traceql": 0.0.6 + "@grafana/lezer-traceql": 0.0.7 "@grafana/monaco-logql": ^0.0.7 "@grafana/runtime": "workspace:*" "@grafana/scenes": ^1.15.0