Tempo: Copy trace query to TraceQL tab (#79935)

* Copy trace query to TraceQL tab

* Remove datasourceType

* Update button position

* Update test

* Remove extra check

* Update text
This commit is contained in:
Joey
2024-01-04 16:10:09 +00:00
committed by GitHub
parent e508c83538
commit ef6b35ad3b
4 changed files with 55 additions and 13 deletions

View File

@@ -193,6 +193,8 @@ class TempoQueryFieldComponent extends React.PureComponent<Props, State> {
query={query} query={query}
onChange={onChange} onChange={onChange}
onBlur={this.props.onBlur} onBlur={this.props.onBlur}
app={app}
onClearResults={this.onClearResults}
/> />
)} )}
{query.queryType === 'serviceMap' && ( {query.queryType === 'serviceMap' && (

View File

@@ -70,6 +70,7 @@ describe('TraceQLSearch', () => {
const onChange = (q: TempoQuery) => { const onChange = (q: TempoQuery) => {
query = q; query = q;
}; };
const onClearResults = jest.fn();
beforeEach(() => { beforeEach(() => {
jest.useFakeTimers(); jest.useFakeTimers();
@@ -83,7 +84,9 @@ describe('TraceQLSearch', () => {
}); });
it('should update operator when new value is selected in operator input', async () => { it('should update operator when new value is selected in operator input', async () => {
const { container } = render(<TraceQLSearch datasource={datasource} query={query} onChange={onChange} />); const { container } = render(
<TraceQLSearch datasource={datasource} query={query} onChange={onChange} onClearResults={onClearResults} />
);
const minDurationOperator = container.querySelector(`input[aria-label="select min-duration operator"]`); const minDurationOperator = container.querySelector(`input[aria-label="select min-duration operator"]`);
expect(minDurationOperator).not.toBeNull(); expect(minDurationOperator).not.toBeNull();
@@ -101,7 +104,9 @@ describe('TraceQLSearch', () => {
}); });
it('should add new filter when new value is selected in the service name section', async () => { it('should add new filter when new value is selected in the service name section', async () => {
const { container } = render(<TraceQLSearch datasource={datasource} query={query} onChange={onChange} />); const { container } = render(
<TraceQLSearch datasource={datasource} query={query} onChange={onChange} onClearResults={onClearResults} />
);
const serviceNameValue = container.querySelector(`input[aria-label="select service-name value"]`); const serviceNameValue = container.querySelector(`input[aria-label="select service-name value"]`);
expect(serviceNameValue).not.toBeNull(); expect(serviceNameValue).not.toBeNull();
expect(serviceNameValue).toBeInTheDocument(); expect(serviceNameValue).toBeInTheDocument();
@@ -136,7 +141,9 @@ describe('TraceQLSearch', () => {
} as TempoDatasource; } as TempoDatasource;
datasource.languageProvider = new TempoLanguageProvider(datasource); datasource.languageProvider = new TempoLanguageProvider(datasource);
await act(async () => { await act(async () => {
const { container } = render(<TraceQLSearch datasource={datasource} query={query} onChange={onChange} />); const { container } = render(
<TraceQLSearch datasource={datasource} query={query} onChange={onChange} onClearResults={onClearResults} />
);
const serviceNameValue = container.querySelector(`input[aria-label="select service-name value"]`); const serviceNameValue = container.querySelector(`input[aria-label="select service-name value"]`);
expect(serviceNameValue).toBeNull(); expect(serviceNameValue).toBeNull();
expect(serviceNameValue).not.toBeInTheDocument(); expect(serviceNameValue).not.toBeInTheDocument();
@@ -145,7 +152,9 @@ describe('TraceQLSearch', () => {
it('should not render group by when feature toggle is not enabled', async () => { it('should not render group by when feature toggle is not enabled', async () => {
await waitFor(() => { await waitFor(() => {
render(<TraceQLSearch datasource={datasource} query={query} onChange={onChange} />); render(
<TraceQLSearch datasource={datasource} query={query} onChange={onChange} onClearResults={onClearResults} />
);
const groupBy = screen.queryByText('Aggregate by'); const groupBy = screen.queryByText('Aggregate by');
expect(groupBy).toBeNull(); expect(groupBy).toBeNull();
expect(groupBy).not.toBeInTheDocument(); expect(groupBy).not.toBeInTheDocument();
@@ -155,7 +164,9 @@ describe('TraceQLSearch', () => {
it('should render group by when feature toggle enabled', async () => { it('should render group by when feature toggle enabled', async () => {
config.featureToggles.metricsSummary = true; config.featureToggles.metricsSummary = true;
await waitFor(() => { await waitFor(() => {
render(<TraceQLSearch datasource={datasource} query={query} onChange={onChange} />); render(
<TraceQLSearch datasource={datasource} query={query} onChange={onChange} onClearResults={onClearResults} />
);
const groupBy = screen.queryByText('Aggregate by'); const groupBy = screen.queryByText('Aggregate by');
expect(groupBy).not.toBeNull(); expect(groupBy).not.toBeNull();
expect(groupBy).toBeInTheDocument(); expect(groupBy).toBeInTheDocument();

View File

@@ -1,10 +1,9 @@
import { css } from '@emotion/css'; import { css } from '@emotion/css';
import React, { useCallback, useEffect, useState } from 'react'; import React, { useCallback, useEffect, useState } from 'react';
import { GrafanaTheme2 } from '@grafana/data'; import { CoreApp, GrafanaTheme2 } from '@grafana/data';
import { EditorRow } from '@grafana/experimental'; import { config, FetchError, getTemplateSrv, reportInteraction } from '@grafana/runtime';
import { config, FetchError, getTemplateSrv } from '@grafana/runtime'; import { Alert, Button, HorizontalGroup, Select, useStyles2 } from '@grafana/ui';
import { Alert, HorizontalGroup, Select, useStyles2 } from '@grafana/ui';
import { createErrorNotification } from '../../../../core/copy/appNotification'; import { createErrorNotification } from '../../../../core/copy/appNotification';
import { notifyApp } from '../../../../core/reducers/appNotification'; import { notifyApp } from '../../../../core/reducers/appNotification';
@@ -28,11 +27,13 @@ interface Props {
query: TempoQuery; query: TempoQuery;
onChange: (value: TempoQuery) => void; onChange: (value: TempoQuery) => void;
onBlur?: () => void; onBlur?: () => void;
onClearResults: () => void;
app?: CoreApp;
} }
const hardCodedFilterIds = ['min-duration', 'max-duration', 'status']; const hardCodedFilterIds = ['min-duration', 'max-duration', 'status'];
const TraceQLSearch = ({ datasource, query, onChange }: Props) => { const TraceQLSearch = ({ datasource, query, onChange, onClearResults, app }: Props) => {
const styles = useStyles2(getStyles); const styles = useStyles2(getStyles);
const [error, setError] = useState<Error | FetchError | null>(null); const [error, setError] = useState<Error | FetchError | null>(null);
@@ -214,9 +215,30 @@ const TraceQLSearch = ({ datasource, query, onChange }: Props) => {
<GroupByField datasource={datasource} onChange={onChange} query={query} isTagsLoading={isTagsLoading} /> <GroupByField datasource={datasource} onChange={onChange} query={query} isTagsLoading={isTagsLoading} />
)} )}
</div> </div>
<EditorRow> <div className={styles.rawQueryContainer}>
<RawQuery query={templateSrv.replace(traceQlQuery)} lang={{ grammar: traceqlGrammar, name: 'traceql' }} /> <RawQuery query={templateSrv.replace(traceQlQuery)} lang={{ grammar: traceqlGrammar, name: 'traceql' }} />
</EditorRow> <Button
variant="secondary"
size="sm"
onClick={() => {
reportInteraction('grafana_traces_copy_to_traceql_clicked', {
app: app ?? '',
grafana_version: config.buildInfo.version,
location: 'search_tab',
});
onClearResults();
const traceQlQuery = generateQueryFromFilters(query.filters || []);
onChange({
...query,
query: traceQlQuery,
queryType: 'traceql',
});
}}
>
Edit in TraceQL
</Button>
</div>
<TempoQueryBuilderOptions onChange={onChange} query={query} /> <TempoQueryBuilderOptions onChange={onChange} query={query} />
</div> </div>
{error ? ( {error ? (
@@ -242,4 +264,11 @@ const getStyles = (theme: GrafanaTheme2) => ({
flex-wrap: wrap; flex-wrap: wrap;
flex-direction: column; flex-direction: column;
`, `,
rawQueryContainer: css({
alignItems: 'center',
backgroundColor: theme.colors.background.secondary,
display: 'flex',
justifyContent: 'space-between',
padding: theme.spacing(1),
}),
}); });

View File

@@ -48,9 +48,9 @@ export function QueryEditor(props: Props) {
size="sm" size="sm"
onClick={() => { onClick={() => {
reportInteraction('grafana_traces_copy_to_traceql_clicked', { reportInteraction('grafana_traces_copy_to_traceql_clicked', {
datasourceType: 'tempo',
app: props.app ?? '', app: props.app ?? '',
grafana_version: config.buildInfo.version, grafana_version: config.buildInfo.version,
location: 'traceql_tab',
}); });
props.onClearResults(); props.onClearResults();