mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
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:
@@ -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'] },
|
||||
],
|
||||
})
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user