Loki: Rewrite getHighlighterExpressionsFromQuery to use Loki parser (#53316)

* refactor(loki): use Loki parser in getHighlighterExpressionsFromQuery

* fix(highlighter): update regex to properly identify term quotes

* refactor(highlighter): determine quote type by string comparison

* Chore: remove empty line
This commit is contained in:
Matias Chomicki 2022-08-09 15:23:14 +02:00 committed by GitHub
parent d638cd4fd7
commit 27e2953951
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 46 deletions

View File

@ -98,6 +98,15 @@ describe('getHighlighterExpressionsFromQuery', () => {
it('does not remove backslash escaping if regex filter operator and backticks are used', () => { it('does not remove backslash escaping if regex filter operator and backticks are used', () => {
expect(getHighlighterExpressionsFromQuery('{foo="bar"} |~ `\\w+`')).toEqual(['\\w+']); expect(getHighlighterExpressionsFromQuery('{foo="bar"} |~ `\\w+`')).toEqual(['\\w+']);
}); });
it.each`
input | expected
${'`"test"`'} | ${'"test"'}
${'"`test`"'} | ${'`test`'}
${'`"test"a`'} | ${'"test"a'}
`('should correctly identify the type of quote used in the term', ({ input, expected }) => {
expect(getHighlighterExpressionsFromQuery(`{foo="bar"} |= ${input}`)).toEqual([expected]);
});
}); });
describe('getNormalizedLokiQuery', () => { describe('getNormalizedLokiQuery', () => {

View File

@ -1,6 +1,7 @@
import { SyntaxNode } from '@lezer/common';
import { escapeRegExp } from 'lodash'; import { escapeRegExp } from 'lodash';
import { parser } from '@grafana/lezer-logql'; import { parser, LineFilter, PipeExact, PipeMatch, Filter, String } from '@grafana/lezer-logql';
import { ErrorName } from '../prometheus/querybuilder/shared/parsingUtils'; import { ErrorName } from '../prometheus/querybuilder/shared/parsingUtils';
@ -15,45 +16,39 @@ export function formatQuery(selector: string | undefined): string {
* E.g., `{} |= foo |=bar != baz` returns `['foo', 'bar']`. * E.g., `{} |= foo |=bar != baz` returns `['foo', 'bar']`.
*/ */
export function getHighlighterExpressionsFromQuery(input: string): string[] { export function getHighlighterExpressionsFromQuery(input: string): string[] {
let expression = input;
const results = []; const results = [];
// Consume filter expression from left to right const tree = parser.parse(input);
while (expression) { const filters: SyntaxNode[] = [];
const filterStart = expression.search(/\|=|\|~|!=|!~/); tree.iterate({
// Nothing more to search enter: (type, from, to, get): void => {
if (filterStart === -1) { if (type.id === LineFilter) {
break; filters.push(get());
} }
// Drop terms for negative filters },
const filterOperator = expression.slice(filterStart, filterStart + 2); });
const skip = expression.slice(filterStart).search(/!=|!~/) === 0;
expression = expression.slice(filterStart + 2); for (let filter of filters) {
if (skip) { const pipeExact = filter.getChild(Filter)?.getChild(PipeExact);
const pipeMatch = filter.getChild(Filter)?.getChild(PipeMatch);
const string = filter.getChild(String);
if ((!pipeExact && !pipeMatch) || !string) {
continue; continue;
} }
// Check if there is more chained, by just looking for the next pipe-operator
const filterEnd = expression.search(/\|/); const filterTerm = input.substring(string.from, string.to).trim();
let filterTerm; const backtickedTerm = filterTerm[0] === '`';
if (filterEnd === -1) { const unwrappedFilterTerm = filterTerm.substring(1, filterTerm.length - 1);
filterTerm = expression.trim();
} else { if (!unwrappedFilterTerm) {
filterTerm = expression.slice(0, filterEnd).trim(); continue;
expression = expression.slice(filterEnd);
} }
const quotedTerm = filterTerm.match(/"(.*?)"/);
const backtickedTerm = filterTerm.match(/`(.*?)`/);
const term = quotedTerm || backtickedTerm;
if (term) {
const unwrappedFilterTerm = term[1];
const regexOperator = filterOperator === '|~';
let resultTerm = ''; let resultTerm = '';
// Only filter expressions with |~ operator are treated as regular expressions // Only filter expressions with |~ operator are treated as regular expressions
if (regexOperator) { if (pipeMatch) {
// When using backticks, Loki doesn't require to escape special characters and we can just push regular expression to highlights array // When using backticks, Loki doesn't require to escape special characters and we can just push regular expression to highlights array
// When using quotes, we have extra backslash escaping and we need to replace \\ with \ // When using quotes, we have extra backslash escaping and we need to replace \\ with \
resultTerm = backtickedTerm ? unwrappedFilterTerm : unwrappedFilterTerm.replace(/\\\\/g, '\\'); resultTerm = backtickedTerm ? unwrappedFilterTerm : unwrappedFilterTerm.replace(/\\\\/g, '\\');
@ -65,11 +60,7 @@ export function getHighlighterExpressionsFromQuery(input: string): string[] {
if (resultTerm) { if (resultTerm) {
results.push(resultTerm); results.push(resultTerm);
} }
} else {
return results;
} }
}
return results; return results;
} }