Submit
Path:
~
/
/
usr
/
share
/
grafana
/
public
/
app
/
plugins
/
datasource
/
loki
/
File Content:
queryUtils.test.ts
import { String } from '@grafana/lezer-logql'; import { createLokiDatasource } from './__mocks__/datasource'; import { getHighlighterExpressionsFromQuery, getLokiQueryType, isLogsQuery, isQueryWithLabelFormat, isQueryWithParser, isQueryWithError, parseToNodeNamesArray, getParserFromQuery, obfuscate, requestSupportsSplitting, isQueryWithRangeVariable, isQueryPipelineErrorFiltering, getLogQueryFromMetricsQuery, getNormalizedLokiQuery, getNodePositionsFromQuery, formatLogqlQuery, getLogQueryFromMetricsQueryAtPosition, } from './queryUtils'; import { LokiQuery, LokiQueryType } from './types'; describe('getHighlighterExpressionsFromQuery', () => { it('returns no expressions for empty query', () => { expect(getHighlighterExpressionsFromQuery('')).toEqual([]); }); it('returns no expression for query with empty filter ', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= ``')).toEqual([]); }); it('returns no expression for query with empty filter and parser', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= `` | json count="counter" | __error__=``')).toEqual([]); }); it('returns no expression for query with empty filter and chained filter', () => { expect( getHighlighterExpressionsFromQuery('{foo="bar"} |= `` |= `highlight` | json count="counter" | __error__=``') ).toEqual(['highlight']); }); it('returns no expression for query with empty filter, chained and regex filter', () => { expect( getHighlighterExpressionsFromQuery( '{foo="bar"} |= `` |= `highlight` |~ `high.ight` | json count="counter" | __error__=``' ) ).toEqual(['highlight', 'high.ight']); }); it('returns no expression for query with empty filter, chained and regex quotes filter', () => { expect( getHighlighterExpressionsFromQuery( '{foo="bar"} |= `` |= `highlight` |~ "highlight\\\\d" | json count="counter" | __error__=``' ) ).toEqual(['highlight', 'highlight\\d']); }); it('returns an expression for query with filter using quotes', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x"')).toEqual(['x']); }); it('returns an expression for query with filter using backticks', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= `x`')).toEqual(['x']); }); it('returns expressions for query with filter chain', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" |~ "y"')).toEqual(['x', 'y']); }); it('returns expressions for query with filter chain using both backticks and quotes', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" |~ `y`')).toEqual(['x', 'y']); }); it('returns expression for query with log parser', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" | logfmt')).toEqual(['x']); }); it('returns expressions for query with filter chain followed by log parser', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" |~ "y" | logfmt')).toEqual(['x', 'y']); }); it('returns drops expressions for query with negative filter chain using quotes', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" != "y"')).toEqual(['x']); }); it('returns expressions for query with filter chain using backticks', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= `x` |~ `y`')).toEqual(['x', 'y']); }); it('returns expressions for query with filter chain using quotes and backticks', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x" |~ `y`')).toEqual(['x', 'y']); }); it('returns null if filter term is not wrapped in double quotes', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= x')).toEqual([]); }); it('escapes filter term if regex filter operator is not used', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |= "x[yz].w"')).toEqual(['x\\[yz\\]\\.w']); }); it('does not escape filter term if regex filter operator is used', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |~ "x[yz].w" |~ "z.+"')).toEqual(['x[yz].w', 'z.+']); }); it('removes extra backslash escaping if regex filter operator and quotes are used', () => { expect(getHighlighterExpressionsFromQuery('{foo="bar"} |~ "\\\\w+"')).toEqual(['\\w+']); }); it('does not remove backslash escaping if regex filter operator and backticks are used', () => { 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]); }); it.each(['|=', '|~'])('returns multiple expressions when using or statements', (op: string) => { expect(getHighlighterExpressionsFromQuery(`{app="frontend"} ${op} "line" or "text"`)).toEqual(['line', 'text']); }); it.each(['|=', '|~'])('returns multiple expressions when using or statements and ip filters', (op: string) => { expect(getHighlighterExpressionsFromQuery(`{app="frontend"} ${op} "line" or ip("10.0.0.1")`)).toEqual(['line']); }); }); describe('getNormalizedLokiQuery', () => { it('removes deprecated instant property', () => { const input: LokiQuery = { refId: 'A', expr: 'test1', instant: true }; const output = getNormalizedLokiQuery(input); expect(output).toStrictEqual({ refId: 'A', expr: 'test1', queryType: LokiQueryType.Instant }); }); it('removes deprecated range property', () => { const input: LokiQuery = { refId: 'A', expr: 'test1', range: true }; const output = getNormalizedLokiQuery(input); expect(output).toStrictEqual({ refId: 'A', expr: 'test1', queryType: LokiQueryType.Range }); }); it('removes deprecated range and instant properties if query with queryType', () => { const input: LokiQuery = { refId: 'A', expr: 'test1', range: true, instant: false, queryType: LokiQueryType.Range }; const output = getNormalizedLokiQuery(input); expect(output).toStrictEqual({ refId: 'A', expr: 'test1', queryType: LokiQueryType.Range }); }); }); describe('getLokiQueryType', () => { function expectCorrectQueryType(inputProps: Object, outputQueryType: LokiQueryType) { const input: LokiQuery = { refId: 'A', expr: 'test1', ...inputProps }; const output = getLokiQueryType(input); expect(output).toStrictEqual(outputQueryType); } it('handles no props case', () => { expectCorrectQueryType({}, LokiQueryType.Range); }); it('handles old-style instant case', () => { expectCorrectQueryType({ instant: true, range: false }, LokiQueryType.Instant); }); it('handles old-style range case', () => { expectCorrectQueryType({ instant: false, range: true }, LokiQueryType.Range); }); it('handles new+old style instant', () => { expectCorrectQueryType({ instant: true, range: false, queryType: LokiQueryType.Range }, LokiQueryType.Range); }); it('handles new+old style range', () => { expectCorrectQueryType({ instant: false, range: true, queryType: LokiQueryType.Instant }, LokiQueryType.Instant); }); it('handles new<>old conflict (new wins), range', () => { expectCorrectQueryType({ instant: false, range: true, queryType: LokiQueryType.Range }, LokiQueryType.Range); }); it('handles new<>old conflict (new wins), instant', () => { expectCorrectQueryType({ instant: true, range: false, queryType: LokiQueryType.Instant }, LokiQueryType.Instant); }); it('handles invalid new, range', () => { expectCorrectQueryType({ queryType: 'invalid' }, LokiQueryType.Range); }); it('handles invalid new, when old-range exists, use old', () => { expectCorrectQueryType({ instant: false, range: true, queryType: 'invalid' }, LokiQueryType.Range); }); it('handles invalid new, when old-instant exists, use old', () => { expectCorrectQueryType({ instant: true, range: false, queryType: 'invalid' }, LokiQueryType.Instant); }); }); describe('isQueryWithError', () => { it('returns false if invalid query', () => { expect(isQueryWithError('{job="grafana')).toBe(true); }); it('returns true if valid query', () => { expect(isQueryWithError('{job="grafana"}')).toBe(false); }); }); describe('parseToNodeNamesArray', () => { it('returns on empty query', () => { expect(parseToNodeNamesArray('{}')).toEqual(['LogQL', 'Expr', 'LogExpr', 'Selector', '⚠']); }); it('returns on invalid query', () => { expect(parseToNodeNamesArray('{job="grafana"')).toEqual([ 'LogQL', 'Expr', 'LogExpr', 'Selector', 'Matchers', 'Matcher', 'Identifier', 'Eq', 'String', '⚠', ]); }); it('returns on valid query', () => { expect(parseToNodeNamesArray('{job="grafana"}')).toEqual([ 'LogQL', 'Expr', 'LogExpr', 'Selector', 'Matchers', 'Matcher', 'Identifier', 'Eq', 'String', ]); }); }); describe('obfuscate', () => { it('obfuscates on invalid query', () => { expect(obfuscate('{job="grafana"')).toEqual('{Identifier=String'); }); it('obfuscates on valid query', () => { expect( obfuscate('sum(sum_over_time({test="test"} |= `` | logfmt | __error__=`` | unwrap test | __error__=`` [10m]))') ).toEqual( 'sum(sum_over_time({Identifier=String} |= String | logfmt | __error__=String | unwrap Identifier | __error__=String [10m]))' ); }); it('obfuscates on arithmetic operation', () => { expect(obfuscate('2 + 3')).toEqual('Number + Number'); }); it('obfuscates a comment', () => { expect(obfuscate('{job="grafana"} # test comment')).toEqual('{Identifier=String} LineComment'); }); it('does not obfuscate interval variables', () => { expect( obfuscate( 'sum(quantile_over_time(0.5, {label="$var"} | logfmt | __error__=`` | unwrap latency | __error__=`` [$__interval]))' ) ).toEqual( 'sum(quantile_over_time(Number, {Identifier=String} | logfmt | __error__=String | unwrap Identifier | __error__=String [$__interval]))' ); }); }); describe('isLogsQuery', () => { it('returns false if metrics query', () => { expect(isLogsQuery('rate({job="grafana"}[5m])')).toBe(false); }); it('returns true if valid query', () => { expect(isLogsQuery('{job="grafana"}')).toBe(true); }); }); describe('isQueryWithParser', () => { it('returns false if query without parser', () => { expect(isQueryWithParser('rate({job="grafana" |= "error" }[5m])')).toEqual({ parserCount: 0, queryWithParser: false, }); }); it('returns true if log query with parser', () => { expect(isQueryWithParser('{job="grafana"} | json')).toEqual({ parserCount: 1, queryWithParser: true }); }); it('returns true if metric query with parser', () => { expect(isQueryWithParser('rate({job="grafana"} | json [5m])')).toEqual({ parserCount: 1, queryWithParser: true }); }); it('returns true if query with json parser with expressions', () => { expect(isQueryWithParser('rate({job="grafana"} | json foo="bar", bar="baz" [5m])')).toEqual({ parserCount: 1, queryWithParser: true, }); }); }); describe('isQueryWithLabelFormat', () => { it('returns true if log query with label format', () => { expect(isQueryWithLabelFormat('{job="grafana"} | label_format level=lvl')).toBe(true); }); it('returns true if metrics query with label format', () => { expect(isQueryWithLabelFormat('rate({job="grafana"} | label_format level=lvl [5m])')).toBe(true); }); it('returns false if log query without label format', () => { expect(isQueryWithLabelFormat('{job="grafana"} | json')).toBe(false); }); it('returns false if metrics query without label format', () => { expect(isQueryWithLabelFormat('rate({job="grafana"} [5m])')).toBe(false); }); }); describe('isQueryWithRangeVariableDuration', () => { it('identifies queries using $__range variable', () => { expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range])')).toBe(true); }); it('identifies queries using $__range_s variable', () => { expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range_s])')).toBe(true); }); it('identifies queries using $__range_ms variable', () => { expect(isQueryWithRangeVariable('rate({job="grafana"}[$__range_ms])')).toBe(true); }); it('does not return false positives', () => { expect(isQueryWithRangeVariable('rate({job="grafana"} | logfmt | value="$__range" [5m])')).toBe(false); expect(isQueryWithRangeVariable('rate({job="grafana"} | logfmt | value="[$__range]" [5m])')).toBe(false); expect(isQueryWithRangeVariable('rate({job="grafana"} [$range])')).toBe(false); expect(isQueryWithRangeVariable('rate({job="grafana"} [$_range])')).toBe(false); expect(isQueryWithRangeVariable('rate({job="grafana"} [$_range_ms])')).toBe(false); }); }); describe('getParserFromQuery', () => { it('returns no parser', () => { expect(getParserFromQuery('{job="grafana"}')).toBeUndefined(); }); it.each(['json', 'logfmt', 'pattern', 'regexp', 'unpack'])('detects %s parser', (parser: string) => { expect(getParserFromQuery(`{job="grafana"} | ${parser}`)).toBe(parser); expect(getParserFromQuery(`sum(count_over_time({place="luna"} | ${parser} | unwrap counter )) by (place)`)).toBe( parser ); }); it('supports json parser with arguments', () => { // Redundant, but gives us a baseline expect(getParserFromQuery('{job="grafana"} | json')).toBe('json'); expect(getParserFromQuery('{job="grafana"} | json field="otherField"')).toBe('json'); expect(getParserFromQuery('{job="grafana"} | json field="otherField", label="field2"')).toBe('json'); }); it('supports logfmt parser with arguments and flags', () => { // Redundant, but gives us a baseline expect(getParserFromQuery('{job="grafana"} | logfmt')).toBe('logfmt'); expect(getParserFromQuery('{job="grafana"} | logfmt --strict')).toBe('logfmt'); expect(getParserFromQuery('{job="grafana"} | logfmt --strict --keep-empty')).toBe('logfmt'); expect(getParserFromQuery('{job="grafana"} | logfmt field="otherField"')).toBe('logfmt'); expect(getParserFromQuery('{job="grafana"} | logfmt field="otherField", label')).toBe('logfmt'); expect(getParserFromQuery('{job="grafana"} | logfmt --strict field="otherField"')).toBe('logfmt'); expect( getParserFromQuery('{job="grafana"} | logfmt --strict --keep-empty field="otherField", label="field2"') ).toBe('logfmt'); }); }); describe('requestSupportsSplitting', () => { it('hidden requests are not partitioned', () => { const requests: LokiQuery[] = [ { expr: '{a="b"}', refId: 'A', hide: true, }, ]; expect(requestSupportsSplitting(requests)).toBe(false); }); it('special requests are not partitioned', () => { const requests: LokiQuery[] = [ { expr: '{a="b"}', refId: 'do-not-chunk', }, ]; expect(requestSupportsSplitting(requests)).toBe(false); }); it('empty requests are not partitioned', () => { const requests: LokiQuery[] = [ { expr: '', refId: 'A', }, ]; expect(requestSupportsSplitting(requests)).toBe(false); }); it('all other requests are partitioned', () => { const requests: LokiQuery[] = [ { expr: '{a="b"}', refId: 'A', }, { expr: 'count_over_time({a="b"}[1h])', refId: 'B', }, ]; expect(requestSupportsSplitting(requests)).toBe(true); }); }); describe('isQueryPipelineErrorFiltering', () => { it('identifies pipeline error filters', () => { expect(isQueryPipelineErrorFiltering('{job="grafana"} | logfmt | __error__=""')).toBe(true); expect(isQueryPipelineErrorFiltering('{job="grafana"} | logfmt | error=""')).toBe(false); }); }); describe('getLogQueryFromMetricsQuery', () => { it('returns the log query from a metric query', () => { expect(getLogQueryFromMetricsQuery('count_over_time({job="grafana"} | logfmt | label="value" [1m])')).toBe( '{job="grafana"} | logfmt | label="value"' ); expect(getLogQueryFromMetricsQuery('count_over_time({job="grafana"} [1m])')).toBe('{job="grafana"}'); expect( getLogQueryFromMetricsQuery( 'sum(quantile_over_time(0.5, {label="$var"} | logfmt | __error__=`` | unwrap latency | __error__=`` [$__interval]))' ) ).toBe('{label="$var"} | logfmt | __error__=``'); }); it('does not return a query when there is no log query', () => { expect(getLogQueryFromMetricsQuery('1+1')).toBe(''); expect(getLogQueryFromMetricsQuery('count_over_time([1s])')).toBe(''); }); }); describe('getLogQueryFromMetricsQueryAtPosition', () => { it('works like getLogQueryFromMetricsQuery for simple queries', () => { expect( getLogQueryFromMetricsQueryAtPosition('count_over_time({job="grafana"} | logfmt | label="value" [1m])', 57) ).toBe('{job="grafana"} | logfmt | label="value"'); expect(getLogQueryFromMetricsQueryAtPosition('count_over_time({job="grafana"} [1m])', 37)).toBe('{job="grafana"}'); expect( getLogQueryFromMetricsQueryAtPosition( 'sum(quantile_over_time(0.5, {label="$var"} | logfmt | __error__=`` | unwrap latency | __error__=`` [$__interval]))', 45 ) ).toBe('{label="$var"} | logfmt | __error__=``'); }); it.each([ [ 'count_over_time({place="moon"} | json test="test" [1m]) + avg_over_time({place="luna"} | logfmt test="test" [1m])', '{place="moon"} | json test="test"', 49, ], [ 'count_over_time({place="moon"} | json test="test" [1m]) + avg_over_time({place="luna"} | logfmt test="test" [1m])', '{place="luna"} | logfmt test="test"', 107, ], ])('gets the right query for complex queries', (metric: string, log: string, position: number) => { expect(getLogQueryFromMetricsQueryAtPosition(metric, position)).toBe(log); }); }); describe('getNodePositionsFromQuery', () => { it('returns the right amount of positions without type', () => { // LogQL, Expr, LogExpr, Selector, Matchers, Matcher, Identifier, Eq, String expect(getNodePositionsFromQuery('{job="grafana"}').length).toBe(9); }); it('returns the right position of a string in a stream selector', () => { // LogQL, Expr, LogExpr, Selector, Matchers, Matcher, Identifier, Eq, String const nodePositions = getNodePositionsFromQuery('{job="grafana"}', [String]); expect(nodePositions.length).toBe(1); expect(nodePositions[0].from).toBe(5); expect(nodePositions[0].to).toBe(14); }); it('returns an empty array with a wrong expr', () => { // LogQL, Expr, LogExpr, Selector, Matchers, Matcher, Identifier, Eq, String const nodePositions = getNodePositionsFromQuery('not loql', [String]); expect(nodePositions.length).toBe(0); }); }); describe('formatLogqlQuery', () => { const ds = createLokiDatasource(); it('formats a logs query', () => { expect(formatLogqlQuery('{job="grafana"}', ds)).toBe('{job="grafana"}'); }); it('formats a metrics query', () => { expect(formatLogqlQuery('count_over_time({job="grafana"}[1m])', ds)).toBe( 'count_over_time(\n {job="grafana"}\n [1m]\n)' ); }); it('formats a metrics query with variables', () => { // mock the interpolateString return value so it passes the isValid check ds.interpolateString = jest.fn(() => 'rate({job="grafana"}[1s])'); expect(formatLogqlQuery('rate({job="grafana"}[$__range])', ds)).toBe('rate(\n {job="grafana"}\n [$__range]\n)'); expect(formatLogqlQuery('rate({job="grafana"}[$__interval])', ds)).toBe( 'rate(\n {job="grafana"}\n [$__interval]\n)' ); }); });
Edit
Rename
Chmod
Delete
FILE
FOLDER
INFO
Name
Size
Permission
Action
__mocks__
---
0755
components
---
0755
configuration
---
0755
docs
---
0755
img
---
0755
migrations
---
0755
querybuilder
---
0755
LanguageProvider.test.ts
23374 bytes
0644
LanguageProvider.ts
13424 bytes
0644
LiveStreams.test.ts
7021 bytes
0644
LiveStreams.ts
2641 bytes
0644
LogContextProvider.test.ts
21477 bytes
0644
LogContextProvider.ts
14192 bytes
0644
LokiVariableSupport.test.ts
3307 bytes
0644
LokiVariableSupport.ts
984 bytes
0644
README.md
127 bytes
0644
backendResultTransformer.test.ts
5537 bytes
0644
backendResultTransformer.ts
5248 bytes
0644
dataquery.cue
1633 bytes
0644
dataquery.gen.ts
1269 bytes
0644
datasource.test.ts
67600 bytes
0644
datasource.ts
42882 bytes
0644
getDerivedFields.test.ts
6291 bytes
0644
getDerivedFields.ts
4351 bytes
0644
languageUtils.test.ts
4461 bytes
0644
languageUtils.ts
5123 bytes
0644
language_utils.test.ts
1463 bytes
0644
lineParser.test.ts
1846 bytes
0644
lineParser.ts
912 bytes
0644
liveStreamsResultTransformer.test.ts
3396 bytes
0644
liveStreamsResultTransformer.ts
2624 bytes
0644
logsTimeSplitting.test.ts
1463 bytes
0644
logsTimeSplitting.ts
1661 bytes
0644
makeTableFrames.test.ts
3834 bytes
0644
makeTableFrames.ts
2415 bytes
0644
metricTimeSplitting.test.ts
2925 bytes
0644
metricTimeSplitting.ts
1586 bytes
0644
modifyQuery.test.ts
27450 bytes
0644
modifyQuery.ts
20058 bytes
0644
module.test.ts
2975 bytes
0644
module.ts
809 bytes
0644
plugin.json
756 bytes
0644
queryHints.test.ts
7526 bytes
0644
queryHints.ts
4453 bytes
0644
querySplitting.test.ts
20808 bytes
0644
querySplitting.ts
9911 bytes
0644
queryUtils.test.ts
20063 bytes
0644
queryUtils.ts
11174 bytes
0644
responseUtils.test.ts
5757 bytes
0644
responseUtils.ts
4637 bytes
0644
sortDataFrame.test.ts
5184 bytes
0644
sortDataFrame.ts
2708 bytes
0644
streaming.test.ts
1264 bytes
0644
streaming.ts
2939 bytes
0644
syntax.test.ts
3545 bytes
0644
syntax.ts
7646 bytes
0644
tracking.test.ts
6407 bytes
0644
tracking.ts
8353 bytes
0644
types.ts
2568 bytes
0644
N4ST4R_ID | Naxtarrr