Tempo: Group by template vars (#86022)

* Add template variables to group by field

* Add test for interpolation

* Add test to allow selecting template vars

* Show custom value
This commit is contained in:
Joey 2024-04-18 08:52:51 +01:00 committed by GitHub
parent 9614126cb7
commit 306cea7350
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 66 additions and 15 deletions

View File

@ -5,6 +5,7 @@ import React, { useState } from 'react';
import { TraceqlSearchScope } from '../dataquery.gen';
import { TempoDatasource } from '../datasource';
import TempoLanguageProvider from '../language_provider';
import { initTemplateSrv } from '../test_utils';
import { TempoQuery } from '../types';
import { GroupByField } from './GroupByField';
@ -40,6 +41,8 @@ describe('GroupByField', () => {
// Need to use delay: null here to work with fakeTimers
// see https://github.com/testing-library/user-event/issues/833
user = userEvent.setup({ delay: null });
initTemplateSrv([{ name: 'templateVariable1' }, { name: 'templateVariable2' }], {});
});
afterEach(() => {
@ -132,4 +135,21 @@ describe('GroupByField', () => {
expect(groupByFilter?.tag).toBe('http.method');
}
});
it('should allow selecting template variables', async () => {
const { container } = render(
<GroupByField datasource={datasource} query={query} onChange={onChange} isTagsLoading={false} />
);
const tagSelect = container.querySelector(`input[aria-label="Select tag for filter 1"]`);
expect(tagSelect).not.toBeNull();
expect(tagSelect).toBeInTheDocument();
if (tagSelect) {
await user.click(tagSelect);
jest.advanceTimersByTime(1000);
expect(await screen.findByText('$templateVariable1')).toBeInTheDocument();
expect(await screen.findByText('$templateVariable2')).toBeInTheDocument();
}
});
});

View File

@ -11,6 +11,7 @@ import { TempoDatasource } from '../datasource';
import { TempoQuery } from '../types';
import InlineSearchField from './InlineSearchField';
import { withTemplateVariableOptions } from './SearchField';
import { replaceAt } from './utils';
interface Props {
@ -89,15 +90,20 @@ export const GroupByField = (props: Props) => {
<Select
aria-label={`Select tag for filter ${i + 1}`}
isClearable
allowCustomValue
isLoading={isTagsLoading}
key={f.tag}
onChange={(v) => {
updateFilter({ ...f, tag: v?.value });
}}
options={getTags(f)?.map((t) => ({
label: t,
value: t,
}))}
options={withTemplateVariableOptions(
getTags(f)
?.concat(f.tag !== undefined && !getTags(f)?.includes(f.tag) ? [f.tag] : [])
.map((t) => ({
label: t,
value: t,
}))
)}
placeholder="Select tag"
value={f.tag || ''}
/>

View File

@ -126,17 +126,6 @@ const SearchField = ({
operatorList = numberOperators;
}
/**
* Add to a list of options the current template variables.
*
* @param options a list of options
* @returns the list of given options plus the template variables
*/
const withTemplateVariableOptions = (options: SelectableValue[] | undefined) => {
const templateVariables = getTemplateSrv().getVariables();
return [...(options || []), ...templateVariables.map((v) => ({ label: `$${v.name}`, value: `$${v.name}` }))];
};
return (
<>
<HorizontalGroup spacing={'none'} width={'auto'}>
@ -220,4 +209,15 @@ const SearchField = ({
);
};
/**
* Add to a list of options the current template variables.
*
* @param options a list of options
* @returns the list of given options plus the template variables
*/
export const withTemplateVariableOptions = (options: SelectableValue[] | undefined) => {
const templateVariables = getTemplateSrv().getVariables();
return [...(options || []), ...templateVariables.map((v) => ({ label: `$${v.name}`, value: `$${v.name}` }))];
};
export default SearchField;

View File

@ -170,6 +170,16 @@ describe('Tempo data source', () => {
valueType: 'string',
},
],
groupBy: [
{
id: 'groupBy1',
tag: '$interpolationVar',
},
{
id: 'groupBy2',
tag: '$interpolationVar',
},
],
};
}
let templateSrv: TemplateSrv;
@ -202,6 +212,8 @@ describe('Tempo data source', () => {
expect(queries[0].filters[0].value).toBe(textWithPipe);
expect(queries[0].filters[1].value).toBe(text);
expect(queries[0].filters[1].tag).toBe(text);
expect(queries[0].groupBy?.[0].tag).toBe(text);
expect(queries[0].groupBy?.[1].tag).toBe(text);
});
it('when applying template variables', async () => {
@ -214,6 +226,8 @@ describe('Tempo data source', () => {
expect(resp.filters[0].value).toBe(textWithPipe);
expect(resp.filters[1].value).toBe(scopedText);
expect(resp.filters[1].tag).toBe(scopedText);
expect(resp.groupBy?.[0].tag).toBe(scopedText);
expect(resp.groupBy?.[1].tag).toBe(scopedText);
});
it('when serviceMapQuery is an array', async () => {

View File

@ -486,6 +486,17 @@ export class TempoDatasource extends DataSourceWithBackend<TempoQuery, TempoJson
});
}
if (query.groupBy) {
expandedQuery.groupBy = query.groupBy.map((filter) => {
const updatedFilter = {
...filter,
tag: this.templateSrv.replace(filter.tag ?? '', scopedVars),
};
return updatedFilter;
});
}
return {
...expandedQuery,
query: this.templateSrv.replace(query.query ?? '', scopedVars, VariableFormatID.Pipe),