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', () => {
|
it('parses query with matcher label filter', () => {
|
||||||
expect(buildVisualQueryFromString('{app="frontend"} | bar="baz"')).toEqual(
|
expect(buildVisualQueryFromString('{app="frontend"} | bar="baz"')).toEqual(
|
||||||
noErrors({
|
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', () => {
|
it('parses query with with parser', () => {
|
||||||
expect(buildVisualQueryFromString('{app="frontend"} | json')).toEqual(
|
expect(buildVisualQueryFromString('{app="frontend"} | json')).toEqual(
|
||||||
noErrors({
|
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', () => {
|
it('parses query with with simple unwrap', () => {
|
||||||
expect(
|
expect(
|
||||||
buildVisualQueryFromString('sum_over_time({app="frontend"} | logfmt | unwrap bytes_processed [1m])')
|
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', () => {
|
it('parses metrics query with function', () => {
|
||||||
expect(buildVisualQueryFromString('rate({app="frontend"} | json [5m])')).toEqual(
|
expect(buildVisualQueryFromString('rate({app="frontend"} | json [5m])')).toEqual(
|
||||||
noErrors({
|
noErrors({
|
||||||
@@ -323,7 +385,7 @@ describe('buildVisualQueryFromString', () => {
|
|||||||
label: 'app',
|
label: 'app',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
operations: [{ id: 'label_format', params: ['newLabel', '=', 'oldLabel'] }],
|
operations: [{ id: 'label_format', params: ['newLabel', 'oldLabel'] }],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -339,8 +401,8 @@ describe('buildVisualQueryFromString', () => {
|
|||||||
},
|
},
|
||||||
],
|
],
|
||||||
operations: [
|
operations: [
|
||||||
{ id: 'label_format', params: ['newLabel', '=', 'oldLabel'] },
|
{ id: 'label_format', params: ['newLabel', 'oldLabel'] },
|
||||||
{ id: 'label_format', params: ['bar', '=', 'baz'] },
|
{ id: 'label_format', params: ['bar', 'baz'] },
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -58,7 +58,14 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'LineFilter': {
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,11 +75,23 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'LabelFilter': {
|
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;
|
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': {
|
case 'LineFormatExpr': {
|
||||||
visQuery.operations.push(getLineFormat(expr, node));
|
visQuery.operations.push(getLineFormat(expr, node));
|
||||||
@@ -85,7 +104,15 @@ export function handleExpression(expr: string, node: SyntaxNode, context: Contex
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'UnwrapExpr': {
|
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;
|
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 = {
|
const mapFilter: any = {
|
||||||
'|=': '__line_contains',
|
'|=': '__line_contains',
|
||||||
'!=': '__line_contains_not',
|
'!=': '__line_contains_not',
|
||||||
@@ -150,8 +185,10 @@ function getLineFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
|||||||
const filterExpr = handleQuotes(getString(expr, node.getChild('String')));
|
const filterExpr = handleQuotes(getString(expr, node.getChild('String')));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
operation: {
|
||||||
id: mapFilter[filter],
|
id: mapFilter[filter],
|
||||||
params: [filterExpr],
|
params: [filterExpr],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -167,9 +204,20 @@ function getLabelParser(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLabelFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
function getLabelFilter(expr: string, node: SyntaxNode): { operation?: QueryBuilderOperation; error?: string } {
|
||||||
const id = '__label_filter';
|
// 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') {
|
if (node.firstChild!.name === 'UnitFilter') {
|
||||||
const filter = node.firstChild!.firstChild;
|
const filter = node.firstChild!.firstChild;
|
||||||
const label = filter!.firstChild;
|
const label = filter!.firstChild;
|
||||||
@@ -178,47 +226,35 @@ function getLabelFilter(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
|||||||
const valueString = handleQuotes(getString(expr, value));
|
const valueString = handleQuotes(getString(expr, value));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
operation: {
|
||||||
id,
|
id,
|
||||||
params: [getString(expr, label), getString(expr, op), valueString],
|
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
|
// In this case it is Matcher or NumberFilter
|
||||||
const filter = node.firstChild;
|
const filter = node.firstChild;
|
||||||
const label = filter!.firstChild;
|
const label = filter!.firstChild;
|
||||||
const op = label!.nextSibling;
|
const op = label!.nextSibling;
|
||||||
const value = op!.nextSibling;
|
const value = op!.nextSibling;
|
||||||
const params = [getString(expr, label), getString(expr, op), handleQuotes(getString(expr, value))];
|
const params = [getString(expr, label), getString(expr, op), handleQuotes(getString(expr, value))];
|
||||||
//Special case of pipe filtering - no errors
|
|
||||||
|
// Special case of pipe filtering - no errors
|
||||||
if (params.join('') === `__error__=`) {
|
if (params.join('') === `__error__=`) {
|
||||||
return {
|
return {
|
||||||
|
operation: {
|
||||||
id: '__label_filter_no_errors',
|
id: '__label_filter_no_errors',
|
||||||
params: [],
|
params: [],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
operation: {
|
||||||
id,
|
id,
|
||||||
params,
|
params,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function getLineFormat(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
function getLineFormat(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
||||||
@@ -241,17 +277,26 @@ function getLabelFormat(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
id,
|
id,
|
||||||
params: [getString(expr, identifier), getString(expr, op), valueString],
|
params: [getString(expr, identifier), valueString],
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
function getUnwrap(expr: string, node: SyntaxNode): QueryBuilderOperation {
|
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',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
const id = 'unwrap';
|
const id = 'unwrap';
|
||||||
const string = getString(expr, node.getChild('Identifier'));
|
const string = getString(expr, node.getChild('Identifier'));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
operation: {
|
||||||
id,
|
id,
|
||||||
params: [string],
|
params: [string],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -439,3 +484,15 @@ function getLastChildWithSelector(node: SyntaxNode, selector: string) {
|
|||||||
}
|
}
|
||||||
return child;
|
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