Loki: Show error when parsing nodes not supported in visual query builder (#47321)

* Show error when parsing nodes not supported in visual query builder

* Refactor, simplify

* Spell fix

* Spell fix
This commit is contained in:
Ivana Huckova
2022-04-05 17:07:13 +02:00
committed by GitHub
parent b6a184996b
commit 6938d5b975
2 changed files with 168 additions and 49 deletions

View File

@@ -67,6 +67,18 @@ describe('buildVisualQueryFromString', () => {
);
});
it('returns error for query with ip matching line filter', () => {
const context = buildVisualQueryFromString('{app="frontend"} |= ip("192.168.4.5/16")');
expect(context.errors).toEqual([
{
text: 'Matching ip addresses not supported in query builder: |= ip("192.168.4.5/16")',
from: 17,
to: 40,
parentType: 'LineFilters',
},
]);
});
it('parses query with matcher label filter', () => {
expect(buildVisualQueryFromString('{app="frontend"} | bar="baz"')).toEqual(
noErrors({
@@ -127,6 +139,30 @@ describe('buildVisualQueryFromString', () => {
);
});
it('returns error for query with "and", "or", "comma" in label filter', () => {
const context = buildVisualQueryFromString('{app="frontend"} | logfmt | level="error" and job="grafana"');
expect(context.errors).toEqual([
{
text: 'Label filter with comma, "and", "or" not supported in query builder: level="error" and job="grafana"',
from: 28,
to: 59,
parentType: 'PipelineStage',
},
]);
});
it('returns error for query with ip label filter', () => {
const context = buildVisualQueryFromString('{app="frontend"} | logfmt | address=ip("192.168.4.5/16")');
expect(context.errors).toEqual([
{
text: 'IpLabelFilter not supported in query builder: address=ip("192.168.4.5/16")',
from: 28,
to: 56,
parentType: 'PipelineStage',
},
]);
});
it('parses query with with parser', () => {
expect(buildVisualQueryFromString('{app="frontend"} | json')).toEqual(
noErrors({
@@ -142,6 +178,18 @@ describe('buildVisualQueryFromString', () => {
);
});
it('returns error for query with JSON expression parser', () => {
const context = buildVisualQueryFromString('{app="frontend"} | json label="value" ');
expect(context.errors).toEqual([
{
text: 'JsonExpressionParser not supported in visual query builder: json label="value"',
from: 19,
to: 37,
parentType: 'PipelineStage',
},
]);
});
it('parses query with with simple unwrap', () => {
expect(
buildVisualQueryFromString('sum_over_time({app="frontend"} | logfmt | unwrap bytes_processed [1m])')
@@ -163,6 +211,20 @@ describe('buildVisualQueryFromString', () => {
);
});
it('returns error for query with unwrap and conversion operation', () => {
const context = buildVisualQueryFromString(
'sum_over_time({app="frontend"} | logfmt | unwrap duration(label) [5m])'
);
expect(context.errors).toEqual([
{
text: 'Unwrap with conversion operator not supported in query builder: | unwrap duration(label)',
from: 40,
to: 64,
parentType: 'LogRangeExpr',
},
]);
});
it('parses metrics query with function', () => {
expect(buildVisualQueryFromString('rate({app="frontend"} | json [5m])')).toEqual(
noErrors({
@@ -323,7 +385,7 @@ describe('buildVisualQueryFromString', () => {
label: 'app',
},
],
operations: [{ id: 'label_format', params: ['newLabel', '=', 'oldLabel'] }],
operations: [{ id: 'label_format', params: ['newLabel', 'oldLabel'] }],
})
);
});
@@ -339,8 +401,8 @@ describe('buildVisualQueryFromString', () => {
},
],
operations: [
{ id: 'label_format', params: ['newLabel', '=', 'oldLabel'] },
{ id: 'label_format', params: ['bar', '=', 'baz'] },
{ id: 'label_format', params: ['newLabel', 'oldLabel'] },
{ id: 'label_format', params: ['bar', 'baz'] },
],
})
);

View File

@@ -58,7 +58,14 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
}
case 'LineFilter': {
visQuery.operations.push(getLineFilter(expr, node));
const { operation, error } = getLineFilter(expr, node);
if (operation) {
visQuery.operations.push(operation);
}
// Show error for query patterns not supported in visual query builder
if (error) {
context.errors.push(createNotSupportedError(expr, node, error));
}
break;
}
@@ -68,11 +75,23 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
}
case 'LabelFilter': {
visQuery.operations.push(getLabelFilter(expr, node));
const { operation, error } = getLabelFilter(expr, node);
if (operation) {
visQuery.operations.push(operation);
}
// Show error for query patterns not supported in visual query builder
if (error) {
context.errors.push(createNotSupportedError(expr, node, error));
}
break;
}
// Need to figure out JsonExpressionParser
case 'JsonExpressionParser': {
// JsonExpressionParser is not supported in query builder
const error = 'JsonExpressionParser not supported in visual query builder';
context.errors.push(createNotSupportedError(expr, node, error));
}
case 'LineFormatExpr': {
visQuery.operations.push(getLineFormat(expr, node));
@@ -85,7 +104,15 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
}
case 'UnwrapExpr': {
visQuery.operations.push(getUnwrap(expr, node));
const { operation, error } = getUnwrap(expr, node);
if (operation) {
visQuery.operations.push(operation);
}
// Show error for query patterns not supported in visual query builder
if (error) {
context.errors.push(createNotSupportedError(expr, node, error));
}
break;
}
@@ -139,7 +166,15 @@ function getLabel(expr: string, node: SyntaxNode): QueryBuilderLabelFilter {
};
}
function getLineFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
function getLineFilter(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } {
// Check for nodes not supported in visual builder and return error
const ipLineFilter = getAllByType(expr, node, 'Ip');
if (ipLineFilter.length > 0) {
return {
error: 'Matching ip addresses not supported in query builder',
};
}
const mapFilter: any = {
'|=': '__line_contains',
'!=': '__line_contains_not',
@@ -150,8 +185,10 @@ function getLineFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
const filterExpr = handleQuotes(getString(expr, node.getChild('String')));
return {
operation: {
id: mapFilter[filter],
params: [filterExpr],
},
};
}
@@ -167,9 +204,20 @@ function getLabelParser(expr: string, node: SyntaxNode): QueryBuilderOperation {
};
}
function getLabelFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
const id = '__label_filter';
function getLabelFilter(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } {
// Check for nodes not supported in visual builder and return error
if (node.getChild('Or') || node.getChild('And') || node.getChild('Comma')) {
return {
error: 'Label filter with comma, "and", "or" not supported in query builder',
};
}
if (node.firstChild!.name === 'IpLabelFilter') {
return {
error: 'IpLabelFilter not supported in query builder',
};
}
const id = '__label_filter';
if (node.firstChild!.name === 'UnitFilter') {
const filter = node.firstChild!.firstChild;
const label = filter!.firstChild;
@@ -178,48 +226,36 @@ function getLabelFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
const valueString = handleQuotes(getString(expr, value));
return {
operation: {
id,
params: [getString(expr, label), getString(expr, op), valueString],
},
};
}
if (node.firstChild!.name === 'IpLabelFilter') {
// Not implemented in visual query builder yet
const filter = node.firstChild!;
const label = filter.firstChild!;
const op = label.nextSibling!;
const ip = label.nextSibling;
const value = op.nextSibling!;
return {
id,
params: [
getString(expr, label),
getString(expr, op),
handleQuotes(getString(expr, ip)),
handleQuotes(getString(expr, value)),
],
};
} else {
// In this case it is Matcher or NumberFilter
const filter = node.firstChild;
const label = filter!.firstChild;
const op = label!.nextSibling;
const value = op!.nextSibling;
const params = [getString(expr, label), getString(expr, op), handleQuotes(getString(expr, value))];
// Special case of pipe filtering - no errors
if (params.join('') === `__error__=`) {
return {
operation: {
id: '__label_filter_no_errors',
params: [],
},
};
}
return {
operation: {
id,
params,
},
};
}
}
function getLineFormat(expr: string, node: SyntaxNode): QueryBuilderOperation {
const id = 'line_format';
@@ -241,17 +277,26 @@ function getLabelFormat(expr: string, node: SyntaxNode): QueryBuilderOperation {
return {
id,
params: [getString(expr, identifier), getString(expr, op), valueString],
params: [getString(expr, identifier), valueString],
};
}
function getUnwrap(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } {
// Check for nodes not supported in visual builder and return error
if (node.getChild('ConvOp')) {
return {
error: 'Unwrap with conversion operator not supported in query builder',
};
}
function getUnwrap(expr: string, node: SyntaxNode): QueryBuilderOperation {
const id = 'unwrap';
const string = getString(expr, node.getChild('Identifier'));
return {
operation: {
id,
params: [string],
},
};
}
@@ -439,3 +484,15 @@ function getLastChildWithSelector(node: SyntaxNode, selector: string) {
}
return child;
}
/**
* Helper function to enrich error text with information that visual query builder doesn't support that logQL
* @param expr
* @param node
* @param error
*/
function createNotSupportedError(expr: string, node: SyntaxNode, error: string) {
const err = makeError(expr, node);
err.text = `${error}: ${err.text}`;
return err;
}