/usr/share/grafana/public/app/plugins/datasource/loki
import { SyntaxNode } from '@lezer/common'; import { addDropToQuery, addLabelFormatToQuery, addLabelToQuery, addLineFilter, addNoPipelineErrorToQuery, addParserToQuery, getIdentifierInStreamPositions, getStreamSelectorPositions, NodePosition, queryHasFilter, removeCommentsFromQuery, removeLabelFromQuery, } from './modifyQuery'; import { LabelType } from './types'; describe('addLabelToQuery()', () => { it.each` query | description | label | operator | value | expectedResult ${'{x="y"}'} | ${'no label and value'} | ${''} | ${'='} | ${''} | ${''} ${'{x="yy"}'} | ${'simple query'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="yy", bar="baz"}'} ${'{x="yy"}'} | ${'simple query'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="yy", bar="baz"}'} ${'{x="yy"}'} | ${'custom operator'} | ${'bar'} | ${'!='} | ${'baz'} | ${'{x="yy", bar!="baz"}'} ${'rate({}[1m])'} | ${'do not modify ranges'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({bar="baz"}[1m])'} ${'sum by (host) (rate({} [1m]))'} | ${'detect in-order function use'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum by (host) (rate({bar="baz"}[1m]))'} ${'{instance="my-host.com:9100"}'} | ${'selectors with punctuation'} | ${'bar'} | ${'='} | ${'baz'} | ${'{instance="my-host.com:9100", bar="baz"}'} ${'{list="a,b,c"}'} | ${'selectors with punctuation'} | ${'bar'} | ${'='} | ${'baz'} | ${'{list="a,b,c", bar="baz"}'} ${'rate({}[5m]) + rate({}[5m])'} | ${'arithmetical expressions'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({bar="baz"}[5m]) + rate({bar="baz"}[5m])'} ${'avg(rate({x="y"} [$__interval]))+ sum(rate({}[5m]))'} | ${'arithmetical expressions'} | ${'bar'} | ${'='} | ${'baz'} | ${'avg(rate({x="y", bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))'} ${'rate({x="yy"}[5m]) * rate({y="zz",a="bb"}[5m]) * rate({}[5m])'} | ${'arithmetical expressions'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({x="yy", bar="baz"}[5m]) * rate({y="zz", a="bb", bar="baz"}[5m]) * rate({bar="baz"}[5m])'} ${'{x="yy", bar!="baz"}'} | ${'do not add duplicate labels'} | ${'bar'} | ${'!='} | ${'baz'} | ${'{x="yy", bar!="baz"}'} ${'rate({bar="baz"}[1m])'} | ${'do not add duplicate labels'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({bar="baz"}[1m])'} ${'{list="a,b,c", bar="baz"}'} | ${'do not add duplicate labels'} | ${'bar'} | ${'='} | ${'baz'} | ${'{list="a,b,c", bar="baz"}'} ${'avg(rate({bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))'} | ${'do not add duplicate labels'} | ${'bar'} | ${'='} | ${'baz'} | ${'avg(rate({bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))'} ${'{x="y"} |="yy"'} | ${'do not remove filters'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="y", bar="baz"} |="yy"'} ${'{x="y"} |="yy" !~"xx"'} | ${'do not remove filters'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="y", bar="baz"} |="yy" !~"xx"'} ${'{x="y"} or {}'} | ${'metric with logical operators'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="y", bar="baz"} or {bar="baz"}'} ${'{x="y"} and {}'} | ${'metric with logical operators'} | ${'bar'} | ${'='} | ${'baz'} | ${'{x="y", bar="baz"} and {bar="baz"}'} ${'sum(rate({job="foo"}[2m])) by (value $variable)'} | ${'template variables'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({job="foo", bar="baz"}[2m])) by (value $variable)'} ${'rate({x="y"}[${__range_s}s])'} | ${'metric query with range grafana variable'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({x="y", bar="baz"}[${__range_s}s])'} ${'max by (id, name, type) ({type=~"foo|bar|baz-test"}) * on(id) group_right(id, type, name) sum by (id) (rate({} [5m])) * 1000'} | ${'metric query with labels in label list with the group modifier'} | ${'bar'} | ${'='} | ${'baz'} | ${'max by (id, name, type) ({type=~"foo|bar|baz-test", bar="baz"}) * on(id) group_right(id, type, name) sum by (id) (rate({bar="baz"}[5m])) * 1000'} ${'{foo="bar"} | logfmt'} | ${'query with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | bar=`baz`'} ${'{foo="bar"} | logfmt | json'} | ${'query with multiple parsers'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | json | bar=`baz`'} ${'{foo="bar"} | logfmt | x="y"'} | ${'query with parser and label filter'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | x="y" | bar=`baz`'} ${'rate({foo="bar"} | logfmt [5m])'} | ${'metric query with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'rate({foo="bar"} | logfmt | bar=`baz` [5m])'} ${'sum by(host) (rate({foo="bar"} | logfmt | x="y" | line_format "{{.status}}" [5m]))'} | ${'metric query with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum by(host) (rate({foo="bar"} | logfmt | x="y" | bar=`baz` | line_format "{{.status}}" [5m]))'} ${'sum by(host) (rate({foo="bar"} | logfmt | x="y" | label_format process="{{.process}}" [5m]))'} | ${'metric query with parser and label format'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum by(host) (rate({foo="bar"} | logfmt | x="y" | label_format process="{{.process}}" | bar=`baz` [5m]))'} ${'{foo="bar"} | logfmt | x="y" | label_format process="{{.process}}"'} | ${'query with parser and label format'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | x="y" | label_format process="{{.process}}" | bar=`baz`'} ${'{foo="bar"} | logfmt | line_format "{{.status}}"'} | ${'do not add filter to line_format expressions in query with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | bar=`baz` | line_format "{{.status}}"'} ${'{foo="bar"} | logfmt | line_format "{{status}}"'} | ${'do not add filter to line_format expressions in query with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'{foo="bar"} | logfmt | bar=`baz` | line_format "{{status}}"'} ${'{}'} | ${'query without stream selector'} | ${'bar'} | ${'='} | ${'baz'} | ${'{bar="baz"}'} ${'{} | logfmt'} | ${'query without stream selector and with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'{bar="baz"}| logfmt'} ${'{} | x="y"'} | ${'query without stream selector and with label filter'} | ${'bar'} | ${'='} | ${'baz'} | ${'{bar="baz"}| x="y"'} ${'{} | logfmt | x="y"'} | ${'query without stream selector and with parser and label filter'} | ${'bar'} | ${'='} | ${'baz'} | ${'{bar="baz"}| logfmt | x="y"'} ${'sum(rate({x="y"} [5m])) + sum(rate({} | logfmt [5m]))'} | ${'metric query with 1 empty and 1 not empty stream selector with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y", bar="baz"} [5m])) + sum(rate({bar="baz"}| logfmt [5m]))'} ${'sum(rate({x="y"} | logfmt [5m])) + sum(rate({} [5m]))'} | ${'metric query with 1 non-empty and 1 not empty stream selector with parser'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y", bar="baz"} | logfmt [5m])) + sum(rate({bar="baz"}[5m]))'} ${'sum(rate({x="y", bar="baz"} | logfmt [5m])) + sum(rate({x="y", bar="baz"} [5m]))'} | ${'metric query with two duplicate stream selectors'} | ${'x'} | ${'='} | ${'y'} | ${'sum(rate({x="y", bar="baz"} | logfmt [5m])) + sum(rate({x="y", bar="baz"} [5m]))'} ${'{x="yy"}'} | ${'simple query with escaped value'} | ${'bar'} | ${'='} | ${'"baz"'} | ${'{x="yy", bar=""baz""}'} ${'{x="yy"}'} | ${'simple query with escaped value'} | ${'bar'} | ${'='} | ${'\\"baz\\"'} | ${'{x="yy", bar="\\"baz\\""}'} ${'{x="yy"}'} | ${'simple query with an other escaped value'} | ${'bar'} | ${'='} | ${'baz\\\\'} | ${'{x="yy", bar="baz\\\\"}'} ${'{x="yy"}'} | ${'simple query with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'baz\\\\'} | ${'{x="yy", bar~="baz\\\\"}'} ${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value'} | ${'bar'} | ${'='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar=`"baz"`'} ${'{foo=`"bar"`} | logfmt'} | ${'query with label already added to stream selector, doublequotes/backticks'} | ${'foo'} | ${'='} | ${`"bar"`} | ${'{foo=""bar""} | logfmt'} ${'{foo="\\"bar\\""`} | logfmt'} | ${'query with label already added to stream selector, doublequotes/escaped'} | ${'foo'} | ${'='} | ${'\\"bar\\"'} | ${'{foo="\\"bar\\""} | logfmt'} ${'{foo="bar"} | logfmt'} | ${'query with label already added to stream selector, doublequotes unescaped'} | ${'foo'} | ${'='} | ${'bar'} | ${'{foo="bar"} | logfmt'} ${'{foo=`bar`} | logfmt'} | ${'query with label already added to stream selector, backticks'} | ${'foo'} | ${'='} | ${'bar'} | ${'{foo="bar"} | logfmt'} ${'{service_name=`grafana/hosted-grafana-gateway`} | logfmt | caller!=`handler.go:637` '} | ${'query with parser and line filter, backticks'} | ${'service_name'} | ${'='} | ${'grafana/hosted-grafana-gateway'} | ${'{service_name="grafana/hosted-grafana-gateway"} | logfmt | caller!=`handler.go:637` '} ${'{service_name=`grafana/hosted-grafana-gateway`, pod_template_hash!=`5fd76866f4`} | logfmt | caller!=`handler.go:637`'} | ${'query with parser and line filter, multiple stream selectors'} | ${'service_name'} | ${'='} | ${'grafana/hosted-grafana-gateway'} | ${'{service_name="grafana/hosted-grafana-gateway", pod_template_hash!="5fd76866f4"} | logfmt | caller!=`handler.go:637`'} ${'{service_name=`grafana/hosted-grafana-gateway`, x!=`y`} | logfmt | caller!=`handler.go:637`'} | ${'query with parser and line filter, multiple stream selectors, value2'} | ${'x'} | ${'!='} | ${'y'} | ${'{service_name="grafana/hosted-grafana-gateway", x!="y"} | logfmt | caller!=`handler.go:637`'} ${'{service_name="grafana/hosted-grafana-gateway"} | logfmt | caller!=`handler.go:637` '} | ${'query with parser and line filter, doublequotes'} | ${'service_name'} | ${'='} | ${'grafana/hosted-grafana-gateway'} | ${'{service_name="grafana/hosted-grafana-gateway"} | logfmt | caller!=`handler.go:637` '} ${'{foo="bar"} | logfmt'} | ${'query with parser with an other escaped value'} | ${'bar'} | ${'='} | ${'baz\\\\'} | ${'{foo="bar"} | logfmt | bar=`baz\\`'} ${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar~=`"baz"`'} ${'{foo="bar"} | logfmt'} | ${'query with parser with escaped value and regex operator'} | ${'bar'} | ${'~='} | ${'\\"baz\\"'} | ${'{foo="bar"} | logfmt | bar~=`"baz"`'} ${'{foo="bar"} | logfmt'} | ${'query with parser, > operator and number value'} | ${'bar'} | ${'>'} | ${'5'} | ${'{foo="bar"} | logfmt | bar>5'} ${'{foo="bar"} | logfmt'} | ${'query with parser, < operator and non-number value'} | ${'bar'} | ${'<'} | ${'5KiB'} | ${'{foo="bar"} | logfmt | bar<`5KiB`'} ${'sum(rate({x="y"} | logfmt [5m])) + sum(rate({x="z"} | logfmt [5m]))'} | ${'metric query with non empty selectors and parsers'} | ${'bar'} | ${'='} | ${'baz'} | ${'sum(rate({x="y"} | logfmt | bar=`baz` [5m])) + sum(rate({x="z"} | logfmt | bar=`baz` [5m]))'} `( 'should add label to query: $query, description: $description', ({ query, description, label, operator, value, expectedResult }) => { if (description === 'no label and value') { expect(() => { addLabelToQuery(query, label, operator, value); }).toThrow(); } else { expect(addLabelToQuery(query, label, operator, value)).toEqual(expectedResult); } } ); it('should always add label as labelFilter if label type is parsed', () => { expect(addLabelToQuery('{foo="bar"}', 'forcedLabel', '=', 'value', LabelType.Parsed)).toEqual( '{foo="bar"} | forcedLabel=`value`' ); }); it('should always add label as labelFilter if label type is parsed with parser', () => { expect(addLabelToQuery('{foo="bar"} | logfmt', 'forcedLabel', '=', 'value', LabelType.Parsed)).toEqual( '{foo="bar"} | logfmt | forcedLabel=`value`' ); }); it('should always add label as labelFilter if label type is structured', () => { expect(addLabelToQuery('{foo="bar"}', 'forcedLabel', '=', 'value', LabelType.StructuredMetadata)).toEqual( '{foo="bar"} | forcedLabel=`value`' ); }); it('should always add label as labelFilter if label type is structured with parser', () => { expect(addLabelToQuery('{foo="bar"} | logfmt', 'forcedLabel', '=', 'value', LabelType.StructuredMetadata)).toEqual( '{foo="bar"} | logfmt | forcedLabel=`value`' ); }); it('should add label as labelFilter to multiple places if label is StructuredMetadata', () => { expect( addLabelToQuery( 'rate({foo="bar"} [$__auto]) / rate({foo="bar"} [$__auto])', 'forcedLabel', '=', 'value', LabelType.StructuredMetadata ) ).toEqual('rate({foo="bar"} | forcedLabel=`value` [$__auto]) / rate({foo="bar"} | forcedLabel=`value` [$__auto])'); }); }); describe('addParserToQuery', () => { describe('when query had line filter', () => { it('should add parser after line filter', () => { expect(addParserToQuery('{job="grafana"} |= "error"', 'logfmt')).toBe('{job="grafana"} |= "error" | logfmt'); }); it('should add parser after multiple line filters', () => { expect(addParserToQuery('{job="grafana"} |= "error" |= "info" |= "debug"', 'logfmt')).toBe( '{job="grafana"} |= "error" |= "info" |= "debug" | logfmt' ); }); }); describe('when query has no line filters', () => { it('should add parser after log stream selector in logs query', () => { expect(addParserToQuery('{job="grafana"}', 'logfmt')).toBe('{job="grafana"} | logfmt'); }); it('should add parser after log stream selector in metric query', () => { expect(addParserToQuery('rate({job="grafana"} [5m])', 'logfmt')).toBe('rate({job="grafana"} | logfmt [5m])'); }); }); }); describe('addNoPipelineErrorToQuery', () => { it('should add error filtering after logfmt parser', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} | logfmt')).toBe('{job="grafana"} | logfmt | __error__=``'); }); it('should add error filtering after json parser with expressions', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} | json foo="bar", bar="baz"')).toBe( '{job="grafana"} | json foo="bar", bar="baz" | __error__=``' ); }); it('should not add error filtering if no parser', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} |="no parser"')).toBe('{job="grafana"} |="no parser"'); }); }); describe('addLabelFormatToQuery', () => { it('should add label format at the end of log query when parser', () => { expect(addLabelFormatToQuery('{job="grafana"} | logfmt', { originalLabel: 'lvl', renameTo: 'level' })).toBe( '{job="grafana"} | logfmt | label_format level=lvl' ); }); it('should add label format at the end of log query when no parser', () => { expect(addLabelFormatToQuery('{job="grafana"}', { originalLabel: 'lvl', renameTo: 'level' })).toBe( '{job="grafana"} | label_format level=lvl' ); }); it('should add label format at the end of log query when more label parser', () => { expect( addLabelFormatToQuery('{job="grafana"} | logfmt | label_format a=b', { originalLabel: 'lvl', renameTo: 'level' }) ).toBe('{job="grafana"} | logfmt | label_format a=b | label_format level=lvl'); }); it('should add label format at the end of log query part of metrics query', () => { expect( addLabelFormatToQuery('rate({job="grafana"} | logfmt | label_format a=b [5m])', { originalLabel: 'lvl', renameTo: 'level', }) ).toBe('rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m])'); }); it('should add label format at the end of multiple log query part of metrics query', () => { expect( addLabelFormatToQuery( 'rate({job="grafana"} | logfmt | label_format a=b [5m]) + rate({job="grafana"} | logfmt | label_format a=b [5m])', { originalLabel: 'lvl', renameTo: 'level' } ) ).toBe( 'rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m]) + rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m])' ); }); }); describe('addDropToQuery', () => { describe('when query has a line filter', () => { it('should add drop after the line filter', () => { expect(addDropToQuery('{job="grafana"} |= "error"', ['__stream_shard__'])).toBe( '{job="grafana"} |= "error" | drop __stream_shard__' ); }); it('should add the parser after multiple line filters', () => { expect(addDropToQuery('{job="grafana"} |= "error" |= "info" |= "debug"', ['label1', 'label2'])).toBe( '{job="grafana"} |= "error" |= "info" |= "debug" | drop label1, label2' ); }); }); describe('when the query has no line filters', () => { it('should add the parser after the log stream selector in logs query', () => { expect(addDropToQuery('{job="grafana"}', ['label1', 'label2'])).toBe('{job="grafana"} | drop label1, label2'); }); it('should add the parser after the log stream selector in a metric query', () => { expect(addDropToQuery('rate({job="grafana"} [5m])', ['__stream_shard__'])).toBe( 'rate({job="grafana"} | drop __stream_shard__ [5m])' ); }); it('should modify all metric queries', () => { expect( addDropToQuery('sum(count_over_time({job="grafana"} [5m])) + sum(count_over_time({job="grafana"} [5m]))', [ '__stream_shard__', ]) ).toBe( 'sum(count_over_time({job="grafana"} | drop __stream_shard__ [5m])) + sum(count_over_time({job="grafana"} | drop __stream_shard__ [5m]))' ); }); }); }); describe('removeCommentsFromQuery', () => { it.each` query | expectedResult ${'{job="grafana"}#hello'} | ${'{job="grafana"}'} ${'{job="grafana"} | logfmt #hello'} | ${'{job="grafana"} | logfmt '} ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl #hello'} | ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl '} ${`#sum(rate(\n{host="containers"}\n#[1m]))`} | ${`\n{host="containers"}\n`} ${`#sum(rate(\n{host="containers"}\n#| logfmt\n#[1m]))`} | ${`\n{host="containers"}\n\n`} ${'{job="grafana"}\n#hello\n| logfmt'} | ${'{job="grafana"}\n\n| logfmt'} `('strips comments in log query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'{job="grafana"}'} | ${'{job="grafana"}'} ${'{job="grafana"} | logfmt'} | ${'{job="grafana"} | logfmt'} ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl'} | ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl'} `('returns original query if no comments in log query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'count_over_time({job="grafana"}[10m])#hello'} | ${'count_over_time({job="grafana"}[10m])'} ${'count_over_time({job="grafana"} | logfmt[10m])#hello'} | ${'count_over_time({job="grafana"} | logfmt[10m])'} ${'rate({job="grafana"} | logfmt | foo="bar" [10m])#hello'} | ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} `('strips comments in metrics query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'count_over_time({job="grafana"}[10m])#hello'} | ${'count_over_time({job="grafana"}[10m])'} ${'count_over_time({job="grafana"} | logfmt[10m])#hello'} | ${'count_over_time({job="grafana"} | logfmt[10m])'} ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} | ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} `('returns original query if no comments in metrics query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); }); describe('NodePosition', () => { describe('contains', () => { it('should return true if the position is contained within the current position', () => { const position = new NodePosition(5, 10); const containedPosition = new NodePosition(6, 9); const result = position.contains(containedPosition); expect(result).toBe(true); }); it('should return false if the position is not contained within the current position', () => { const position = new NodePosition(5, 10); const outsidePosition = new NodePosition(11, 15); const result = position.contains(outsidePosition); expect(result).toBe(false); }); it('should return true if the position is the same as the current position', () => { const position = new NodePosition(5, 10); const samePosition = new NodePosition(5, 10); const result = position.contains(samePosition); expect(result).toBe(true); }); }); describe('getExpression', () => { it('should return the substring of the query within the given position', () => { const position = new NodePosition(7, 12); const query = 'Hello, world!'; const result = position.getExpression(query); expect(result).toBe('world'); }); it('should return an empty string if the position is out of range', () => { const position = new NodePosition(15, 20); const query = 'Hello, world!'; const result = position.getExpression(query); expect(result).toBe(''); }); }); describe('fromNode', () => { it('should create a new NodePosition instance from a SyntaxNode', () => { const syntaxNode = { from: 5, to: 10, type: 'identifier', } as unknown as SyntaxNode; const result = NodePosition.fromNode(syntaxNode); expect(result).toBeInstanceOf(NodePosition); expect(result.from).toBe(5); expect(result.to).toBe(10); expect(result.type).toBe('identifier'); }); }); }); describe('queryHasFilter', () => { it.each([ ['{job="grafana"}', 'grafana'], ['{job="grafana", foo="bar"}', 'grafana'], ['{foo="bar", job="grafana"}', 'grafana'], ['{job="\\"grafana\\""}', '"grafana"'], ['{foo="bar"} | logfmt | job=`grafana`', 'grafana'], ])('should return true if query has a positive filter', (query: string, value: string) => { expect(queryHasFilter(query, 'job', '=', value)).toBe(true); }); it.each([ ['{job!="grafana"}', 'grafana'], ['{job!="grafana", foo="bar"}', 'grafana'], ['{foo="bar", job!="grafana"}', 'grafana'], ['{job!="\\"grafana\\""}', '"grafana"'], ['{foo="bar"} | logfmt | job!=`grafana`', 'grafana'], ])('should return true if query has a negative filter', (query: string, value: string) => { expect(queryHasFilter(query, 'job', '!=', value)).toBe(true); }); }); describe('removeLabelFromQuery', () => { it.each([ ['{job="grafana"}', 'grafana', '{}'], ['{job="grafana", foo="bar"}', 'grafana', '{foo="bar"}'], ['{foo="bar", job="grafana"}', 'grafana', '{foo="bar"}'], ['{job="\\"grafana\\""}', '"grafana"', '{}'], ['{foo="bar"} | logfmt | job=`grafana`', 'grafana', '{foo="bar"} | logfmt'], ])('should remove a positive label matcher from the query', (query: string, value: string, expected: string) => { expect(removeLabelFromQuery(query, 'job', '=', value)).toBe(expected); }); it.each([ ['{job!="grafana"}', 'grafana', '{}'], ['{job!="grafana", foo="bar"}', 'grafana', '{foo="bar"}'], ['{foo="bar", job!="grafana"}', 'grafana', '{foo="bar"}'], ['{job!="\\"grafana\\""}', '"grafana"', '{}'], ['{foo="bar"} | logfmt | job!=`grafana`', 'grafana', '{foo="bar"} | logfmt'], ])('should remove a negative label matcher from the query', (query: string, value: string, expected: string) => { expect(removeLabelFromQuery(query, 'job', '!=', value)).toBe(expected); }); }); describe.each(['|=', '!='])('addLineFilter type %s', (op: string) => { it('Adds a line filter to a log query', () => { expect(addLineFilter('{place="earth"}', undefined, op)).toBe(`{place="earth"} ${op} \`\``); }); it('Adds a line filter with a value to a log query', () => { expect(addLineFilter('{place="earth"}', 'content', op)).toBe(`{place="earth"} ${op} \`content\``); }); it('Adds a line filter to a metric query', () => { expect(addLineFilter('avg_over_time({place="earth"} [1m])', undefined, op)).toBe( `avg_over_time({place="earth"} ${op} \`\` [1m])` ); }); it('Adds a line filter with a value to a metric query', () => { expect(addLineFilter('avg_over_time({place="earth"} [1m])', 'content', op)).toBe( `avg_over_time({place="earth"} ${op} \`content\` [1m])` ); }); }); describe('getStreamSelectorPositions', () => { it('should parse position of stream selectors', () => { expect( getStreamSelectorPositions('sum(rate({x="y", bar="baz"} | logfmt [5m])) + sum(rate({x="y", bar="baz"} [5m]))') ).toEqual([ { from: 9, to: 27, type: { name: 'Selector', props: {}, id: 40, flags: 0, }, }, { from: 55, to: 73, type: { name: 'Selector', props: {}, id: 40, flags: 0, }, }, ]); }); }); describe('getIdentifierInStreamPositions', () => { it('should parse position of stream selectors', () => { const indexedKeys = ['x', 'bar']; const expr = `sum(rate({${indexedKeys[0]}="y", ${indexedKeys[1]}="baz"} | logfmt | x |= "x=y" |= "bar=baz" [5m])) + sum(rate({${indexedKeys[0]}="y", ${indexedKeys[1]}="baz"} [5m]))`; const identifiers = getIdentifierInStreamPositions(expr); identifiers.forEach((identifier, index) => { expect(identifier.getExpression(expr)).toEqual(indexedKeys[index % 2]); }); expect(identifiers).toEqual([ //x1 { from: 10, to: 11, type: { name: 'Identifier', props: {}, id: 43, flags: 0, }, }, //bar1 { from: 17, to: 20, type: { name: 'Identifier', props: {}, id: 43, flags: 0, }, }, //x2 { from: 82, to: 83, type: { name: 'Identifier', props: {}, id: 43, flags: 0, }, }, //bar2 { from: 89, to: 92, type: { name: 'Identifier', props: {}, id: 43, flags: 0, }, }, ]); }); });
.
Edit
..
Edit
CHANGELOG.md
Edit
LanguageProvider.test.ts
Edit
LanguageProvider.ts
Edit
LiveStreams.test.ts
Edit
LiveStreams.ts
Edit
LogContextProvider.test.ts
Edit
LogContextProvider.ts
Edit
LokiVariableSupport.test.ts
Edit
LokiVariableSupport.ts
Edit
README.md
Edit
backendResultTransformer.test.ts
Edit
backendResultTransformer.ts
Edit
components
Edit
configuration
Edit
dataquery.cue
Edit
dataquery.gen.ts
Edit
datasource.test.ts
Edit
datasource.ts
Edit
dist
Edit
docs
Edit
getDerivedFields.test.ts
Edit
getDerivedFields.ts
Edit
img
Edit
jest-setup.js
Edit
jest.config.js
Edit
languageUtils.test.ts
Edit
languageUtils.ts
Edit
language_utils.test.ts
Edit
lineParser.test.ts
Edit
lineParser.ts
Edit
liveStreamsResultTransformer.test.ts
Edit
liveStreamsResultTransformer.ts
Edit
logsTimeSplitting.test.ts
Edit
logsTimeSplitting.ts
Edit
makeTableFrames.test.ts
Edit
makeTableFrames.ts
Edit
mergeResponses.test.ts
Edit
mergeResponses.ts
Edit
metricTimeSplitting.test.ts
Edit
metricTimeSplitting.ts
Edit
migrations
Edit
mocks
Edit
modifyQuery.test.ts
Edit
modifyQuery.ts
Edit
module.test.ts
Edit
module.ts
Edit
package.json
Edit
plugin.json
Edit
project.json
Edit
queryHints.test.ts
Edit
queryHints.ts
Edit
querySplitting.test.ts
Edit
querySplitting.ts
Edit
queryUtils.test.ts
Edit
queryUtils.ts
Edit
querybuilder
Edit
responseUtils.test.ts
Edit
responseUtils.ts
Edit
shardQuerySplitting.test.ts
Edit
shardQuerySplitting.ts
Edit
sortDataFrame.test.ts
Edit
sortDataFrame.ts
Edit
streaming.test.ts
Edit
streaming.ts
Edit
syntax.test.ts
Edit
syntax.ts
Edit
tracking.test.ts
Edit
tracking.ts
Edit
tsconfig.json
Edit
types.ts
Edit
webpack.config.ts
Edit