diff --git a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts index d96dce6740e..8e3444bcc85 100644 --- a/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts +++ b/public/app/plugins/datasource/tempo/SearchTraceQLEditor/utils.ts @@ -5,6 +5,7 @@ import { getTemplateSrv } from '@grafana/runtime'; import { VariableFormatID } from '@grafana/schema'; import { TraceqlFilter, TraceqlSearchScope } from '../dataquery.gen'; +import { getEscapedSpanNames } from '../datasource'; import TempoLanguageProvider from '../language_provider'; import { Scope } from '../types'; @@ -28,14 +29,20 @@ export const interpolateFilters = (filters: TraceqlFilter[], scopedVars?: Scoped return interpolatedFilters; }; +const isRegExpOperator = (operator: string) => operator === '=~' || operator === '!~'; + +const escapeValues = (values: string[]) => getEscapedSpanNames(values); + export const valueHelper = (f: TraceqlFilter) => { - if (Array.isArray(f.value) && f.value.length > 1) { - return `"${f.value.join('|')}"`; + const value = Array.isArray(f.value) && isRegExpOperator(f.operator!) ? escapeValues(f.value) : f.value; + + if (Array.isArray(value) && value.length > 1) { + return `"${value.join('|')}"`; } if (f.valueType === 'string') { - return `"${f.value}"`; + return `"${value}"`; } - return f.value; + return value; }; export const scopeHelper = (f: TraceqlFilter, lp: TempoLanguageProvider) => { diff --git a/public/app/plugins/datasource/tempo/language_provider.test.ts b/public/app/plugins/datasource/tempo/language_provider.test.ts index 2d6d71ec526..9bd44f33bed 100644 --- a/public/app/plugins/datasource/tempo/language_provider.test.ts +++ b/public/app/plugins/datasource/tempo/language_provider.test.ts @@ -173,6 +173,20 @@ describe('Language_provider', () => { lp.generateQueryFromFilters([{ id: 'foo', tag: 'footag', value: '1234', operator: '>', valueType: 'integer' }]) ).toBe('{.footag>1234}'); }); + it.each([['=~'], ['!~']])('a field with a regexp operator (%s)', (operator) => { + expect( + lp.generateQueryFromFilters([ + { + id: 'span-name', + tag: 'name', + operator, + scope: TraceqlSearchScope.Span, + value: ['api/v2/variants/by-upc/(?P[\\s\\S]*)/$'], + valueType: 'string', + }, + ]) + ).toBe(`{name${operator}"api/v2/variants/by-upc/\\\\(\\\\?P\\\\[\\\\\\\\s\\\\\\\\S\\\\]\\\\*\\\\)/\\\\$"}`); + }); it('two fields with everything filled in', () => { expect( lp.generateQueryFromFilters([